View Javadoc
1   
2   package eu.simuline.util;
3   
4   import eu.simuline.util.MultiSet.Multiplicity;
5   
6   import java.util.Collection;
7   import java.util.Set;
8   import java.util.Map;
9   import java.util.Iterator;
10  
11  /**
12   * Represents an abstract MultiSet based on a {@link Map}. 
13   * 
14   * <p>
15   * addAll's implementation seems strange, 
16   * add seems to be buggy, 
17   * Problem with overflow of multiplicities. 
18   *
19   * @param <MAP>
20   *    the map assigning to each element of this multi-set its multiplicity. 
21   * @param <T>
22   *    the class of the elements of this multi-set. 
23   *
24   * Created: Sun Nov 23 23:32:06 2014
25   *
26   * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
27   * @version 1.0
28   */
29  public abstract class AbstractMultiSet<MAP extends Map<T, Multiplicity>, T> 
30      implements MultiSet<T> {
31  
32      /* -------------------------------------------------------------------- *
33       * inner classes.                                                       *
34       * -------------------------------------------------------------------- */
35  
36      /**
37       * Serves as a wrapper object for a multiplicity {@link #mult}. 
38       * Unlike <code>int</code>s we have real <code>Object</code>s 
39       * which can be stored in a map, e.g. {@link AbstractMultiSet#obj2mult} 
40       * and unlike <code>Integer</code>s these objects are mutable. 
41       */
42      // **** this implementation is not optimal: 
43      // better would be immutable multiplicities 
44      // or just using Integers with checks moved towards enclosing class 
45      public static final class MultiplicityImpl implements Multiplicity {
46  
47  	/* ---------------------------------------------------------------- *
48  	 * fields.                                                          *
49  	 * ---------------------------------------------------------------- */
50  
51  	/**
52  	 * A positive integer representing a multiplicity. 
53  	 */
54  	private int mult;
55  
56  	/* ---------------------------------------------------------------- *
57  	 * constructors.                                                    *
58  	 * ---------------------------------------------------------------- */
59  
60  	/**
61  	 * Creates a new <code>Multiplicity</code> instance 
62  	 * representing a <em>positive</em> multiplicity. 
63  	 *
64  	 * @param mult 
65  	 *    a strictly positive <code>int</code> value 
66  	 *    representing a multiplicity. 
67  	 * @throws IllegalArgumentException 
68  	 *    if <code>mult</code> is not strictly positive. 
69  	 * @see #set(int)
70  	 */
71  	private MultiplicityImpl(int mult) {
72  	    set(mult);
73  	}
74  
75  	/* ---------------------------------------------------------------- *
76  	 * methods.                                                         *
77  	 * ---------------------------------------------------------------- */
78  
79  	protected static MultiplicityImpl create(int mult) {
80  	    return new MultiplicityImpl(mult);
81  	}
82  
83  	/**
84  	 * Sets the multiplicity wrapped by this object 
85  	 * to the specified value. 
86  	 *
87  	 * @param mult 
88  	 *    a strictly positive <code>int</code> value 
89  	 *    representing the old multiplicity. 
90  	 * @throws IllegalArgumentException 
91  	 *    if <code>mult</code> is not strictly positive. 
92  	 */
93  	public int set(int mult) {
94  	    if (mult <= 0) {
95  		throw new IllegalArgumentException
96  		    ("Expected non-negative multiplicity; found " + 
97  		     mult + ". ");
98  	    }
99  	    int oldMult = this.mult;
100 	    this.mult = mult;
101 	    return oldMult;
102 	}
103 
104 	/**
105 	 * Adds the specified multiplicity (which may well be negative) 
106 	 * to the wrapped multiplicity {@link #mult} which is thus modified. 
107 	 *
108 	 * @param mult 
109 	 *    an <code>int</code> such that <code>this.mult + mult &gt; 0</code> 
110 	 *    holds. 
111 	 * @return
112 	 *    the new multiplicity <code>this.mult + mult</code>. 
113 	 * @throws IllegalArgumentException 
114 	 *    if <code>this.mult + mult &lt; 0</code> holds. 
115 	 * @throws IllegalStateException 
116 	 *    if <code>this.mult + mult == 0</code> holds. 
117 	 *    This cannot occur: if it does this is a bug within this class. 
118 	 */
119 	public int add(int mult) {
120 	    // **** not completely ok: overflow 
121 	    this.mult += mult;
122 	    if (this.mult <= 0) {
123 		if (this.mult == 0) {
124 		    throw new IllegalStateException
125 			("should not occur: removed element implicitely. " );
126 		}
127 
128 		this.mult -= mult;
129 		throw new IllegalArgumentException
130 		    ("Resulting multiplicity " + 
131 		     this.mult + " + " + mult + 
132 		     " should be non-negative. ");
133 	    }
134 	    return this.mult;
135 	}
136 
137 	/**
138 	 * Returns the wrapped multiplicity. 
139 	 *
140 	 * @return 
141 	 *    {@link #mult}. 
142 	 */
143 	public int get() {
144 	    return this.mult;
145 	}
146 
147 	/**
148 	 * Defines the natural ordering on natural numbers. 
149 	 *
150 	 * @param mult 
151 	 *    a <code>Multiplicity</code> which should in fact 
152 	 *    be another {@link Multiplicity}. 
153 	 * @return 
154 	 *    the difference of the wrapped {@link #mult}-values. 
155 	 * @throws NullPointerException 
156 	 *    for <code>mult == null</code>. 
157 	 * @throws ClassCastException 
158 	 *    if <code>mult</code> is neither <code>null</code> 
159 	 *    nor an instance of {@link Multiplicity}. 
160 	 */
161 	public int compareTo(Multiplicity mult) {
162 	    return this.get() - mult.get();
163 	}
164 
165 	// api-docs provided by javadoc. 
166 	public String toString() {
167 	    return "Multiplicity " + get();
168 	}
169 
170 	/**
171 	 * Returns <code>true</code> if and only if 
172 	 * <code>obj</code> is also an instance of <code>Multiplicity</code> 
173 	 * and if the wrapped multiplicities coincide. 
174 	 *
175 	 * @param obj 
176 	 *    an <code>Object</code> value 
177 	 *    which may well be <code>null</code>. 
178 	 * @return 
179 	 *    a <code>boolean</code> value which indicates 
180 	 *    whether <code>obj</code> is also an instance 
181 	 *    of <code>Multiplicity</code> 
182 	 *    and whether the wrapped multiplicity coincides with this one. 
183 	 * @see #compareTo
184 	 */
185 	public boolean equals(Object obj) {
186 	    if (!(obj instanceof Multiplicity)) {
187 		return false;
188 	    }
189 	    return ((Multiplicity) obj).get() == this.get();
190 	}
191 
192 	// api-docs provided by javadoc. 
193 	public int hashCode() {
194 	    return this.mult;
195 	}
196     } // class Multiplicity 
197 
198     /**
199      * A canonical implementation of {@link MultiSetIterator} 
200      * defining also the methods modifying the underlying {@link MultiSet}, 
201      * namely {@link #remove()}, {@link #setMult(int)} 
202      * and {@link #removeMult(int)}. 
203      *
204      * @param <T>
205      *    the class of the elements of the underlying multi-set. 
206      */
207     protected static class MultiSetIteratorImpl<T> 
208 	implements MultiSetIterator<T> {
209 
210 	/* ---------------------------------------------------------------- *
211 	 * fields.                                                          *
212 	 * ---------------------------------------------------------------- */
213 
214 	/**
215 	 * An iterator on the entries 
216 	 * of the map {@link AbstractMultiSet#obj2mult} 
217 	 * associating each element of the underlying {@link MultiSet} 
218 	 * with its multiplicity. 
219 	 */
220 	private final Iterator<Map.Entry<T, Multiplicity>> entrySetIter;
221 
222 	/**
223 	 * The element returned last by invoking {@link #next()} 
224 	 * in the iterator {@link #entrySetIter} 
225 	 * or <code>null</code> if {@link #next()} has not yet been invoked 
226 	 * or the element returned by the last invocation of {@link #next()} 
227 	 * has been removed in the meantime 
228 	 * invoking a method of this iterator (instance). 
229 	 */
230 	private Map.Entry<T, Multiplicity> last;
231 
232 	/* ---------------------------------------------------------------- *
233 	 * constructors.                                                    *
234 	 * ---------------------------------------------------------------- */
235 
236 	MultiSetIteratorImpl(MultiSet<T> multiSet) {
237 	    this.entrySetIter = multiSet.getSetWithMults().iterator();
238 	    this.last = null;
239 	}
240 
241 	/* ---------------------------------------------------------------- *
242 	 * methods.                                                         *
243 	 * ---------------------------------------------------------------- */
244 
245 	public final boolean hasNext() {
246 	    return this.entrySetIter.hasNext();
247 	}
248 
249 	/**
250 	 * Returns the next element in the iteration 
251 	 * and, as a side effect, sets {@link #last} 
252 	 * with the mapping of that element to its current multiplicity. 
253 	 */
254 	public final T next() {
255 	    this.last = this.entrySetIter.next();
256 	    return this.last.getKey();
257 	}
258 
259 	/**
260 	 * Removes from the underlying {@link MultiSet} 
261 	 * the last element returned by {@link #next()}, 
262 	 * provided that element was not removed in the meantime 
263 	 * and this method is supported by this iterator. 
264 	 * As a side effect, sets {@link #last} to <code>null</code> 
265 	 * indicating that this element has been removed. 
266 	 *
267 	 * @throws UnsupportedOperationException
268 	 *    this implementation does not throw the exception 
269 	 *    but the iterator of an immutable multi set of course do.  
270 	 */
271 	public final void remove()  {
272 	    // throws IllegalStateException if no longer present 
273 	    this.entrySetIter.remove();
274 	    this.last = null;
275 	}
276 
277 	/**
278 	 * Returns the current multiplicity of the element 
279 	 * last read by {@link #next()}, 
280 	 * provided that element was not removed in the meantime. 
281 	 */
282 	public final int getMult() {
283 	    // getMultObj() may throw IllegalStateException 
284 	    return getMultObj().get();
285 	}
286 
287 	public final Multiplicity getMultObj() {
288 	    if (this.last == null) {
289 		// no message as for method remove() 
290 		throw new IllegalStateException();
291 	    }
292 	    assert this.last != null;
293 	    return this.last.getValue();
294 	}
295 	// 
296 	/**
297 	 *
298 	 *
299 	 * @throws UnsupportedOperationException
300 	 *    this implementation does not throw the exception 
301 	 *    but the iterator of an immutable multi set of course do.  
302 	 */
303 	public final int setMult(int mult) {
304 	    // may throw IllegalStateException 
305 	    Multiplicity last = getMultObj();
306 	    assert last != null;
307 	    if (mult == 0) {
308 		int res = last.get();
309 		// may throw UnsupportedOperationException
310 		remove();
311 		return res;
312 	    }
313 	    // may throw IllegalArgumentException, 
314 	    // may throw UnsupportedOperationException
315 	    return last.set(mult);
316 	}
317 
318 	/**
319 	 *
320 	 *
321 	 * @throws UnsupportedOperationException
322 	 *    this implementation does not throw the exception 
323 	 *    but the iterator of an immutable multi set of course do.  
324 	 */
325 	public final int removeMult(int mult)  {
326 	    // may throw IllegalStateException 
327 	    Multiplicity last = getMultObj();
328 	    assert last != null;
329 	    // return value is old multiplicity 
330 	    int oldMult = last.get();
331 	    if (mult == oldMult) {
332 		remove();
333 		return oldMult;
334 	    }
335 
336 	    // may throw an IllegalArgumentException 
337 	    // may throw UnsupportedOperationException
338 	    last.add(-mult);
339 	    return oldMult;
340 	}
341 
342     } // class MultiSetIteratorImpl 
343 
344     /* -------------------------------------------------------------------- *
345      * fields.                                                              *
346      * -------------------------------------------------------------------- */
347 
348     /**
349      * Maps objects to its multiplicities. 
350      * The keys are objects whereas the corresponding values 
351      * are strictly positive <code>Integer</code>s 
352      * representing the corresponding multiplicities. 
353      * If an object is not within this set, 
354      * the corresponding value is <code>null</code>. 
355      * **** maybe even: object not in keyset. 
356      * In the key set no <code>null</code> values may occur. 
357      */
358     protected final MAP obj2mult;
359 
360 
361 
362 
363     /* -------------------------------------------------------------------- *
364      * constructors and creator methods.                                    *
365      * -------------------------------------------------------------------- */
366 
367     public AbstractMultiSet(MAP t2mult) {
368 	this.obj2mult = t2mult;
369     }
370 
371     /* -------------------------------------------------------------------- *
372      * methods.                                                             *
373      * -------------------------------------------------------------------- */
374 
375     // Query Operations
376 
377     /**
378      * Returns the number of pairwise different elements 
379      * in this <code>MultiSet</code>. 
380      * If this <code>MultiSet</code> 
381      * contains more than <tt>Integer.MAX_VALUE</tt> elements, 
382      * returns <tt>Integer.MAX_VALUE</tt>. 
383      * 
384      * @return 
385      *    the number of elements in this <code>MultiSet</code> 
386      *    each multiple element counted as a single one. 
387      * @see #sizeWithMult()
388      */
389     public final int size() {
390 	// works only, since multiplicities 0 are not allowed 
391 	// (null-elements in turn would still work here) 
392 	return this.obj2mult.size();
393     }
394 
395     /**
396      * Returns the number of elements 
397      * in this <code>MultiSet</code> counted with multiplicities. 
398      * If this <code>MultiSet</code> 
399      * contains more than <tt>Integer.MAX_VALUE</tt> elements, 
400      * returns <tt>Integer.MAX_VALUE</tt>. 
401      * 
402      * @return 
403      *    the number of elements in this <code>MultiSet</code> 
404      *    counted with multiplicities, 
405      *    provided this does not exceed {@link Integer#MAX_VALUE}; 
406      *    otherwise just {@link Integer#MAX_VALUE}. 
407      * @see #size()
408      */
409     public final int sizeWithMult() {
410 	int result = 0;
411 	for (Multiplicity mult : this.obj2mult.values()) {
412 	    result += mult.get();
413 	    if (result < 0) {
414 		return Integer.MAX_VALUE;
415 	    }
416 	}
417 	assert result >= 0;
418 	
419 	return result;
420     }
421 
422     /**
423      * Returns whether this multiple set contains no element. 
424      *
425      * @return 
426      *    whether this multiple set contains no element. 
427      */
428     public final boolean isEmpty() {
429 	return this.obj2mult.isEmpty();
430     }
431 
432     /**
433      * Returns a Map.Entry 
434      * representing an element in this <code>MultiSet</code> 
435      * with maximal multiplicity together with this multiplicity, 
436      * except if this set is empty. 
437      * For empty sets, <code>null</code> is returned. 
438      *
439      * @return 
440      *    <ul>
441      *    <li> if this <code>MultiSet</code> is not empty, 
442      *    a <code>Map.Entry</code> object <code>em</code> is returned: 
443      *    <code>em.getKey()</code> is an element of this <code>MultiSet</code> 
444      *    and <code>em.getValue()</code> is a <code>Multiplicity</code> 
445      *    wrapping its multiplicity <code>m = em.getValue().get()</code>. 
446      *    This multiplicity is maximal 
447      *    but if there is more than one such maximal multiplicity, 
448      *    it is not specified which <code>Map.Entry</code> is returned. 
449      *    <p>
450      *    Note that <code>em.getKey()</code> may never be <code>null</code>
451      *
452      *    <li> if this <code>MultiSet</code> is empty, 
453      *    <code>null</code> is returned. 
454      *    </ul>
455      * @see #getObjWithMaxMult()
456      * @see #getMaxMult()
457      */
458     private Map.Entry<T, Multiplicity> getMaxObjWithMult() {
459 	// return value for empty set.
460 	Map.Entry<T, Multiplicity> maxCand = null;
461 
462 	// search for greater value than maxVal
463 	int maxVal = 0;
464 	int cmpVal;
465 	for (Map.Entry<T, Multiplicity> cand : this.obj2mult.entrySet()) {
466 	    cmpVal = cand.getValue().get();
467 	    if (maxVal < cmpVal) {
468 		maxCand = cand;
469 		maxVal = cmpVal;
470 	    }
471 	}
472 	return maxCand;
473     }
474 
475     /**
476      * Returns one of the elements in this multiple set 
477      * with maximal multiplicity. 
478      * The return value is <code>null</code> 
479      * if and only if this set is empty. 
480      *
481      * @return 
482      *    a <code>Object o != null</code> with maximal multiplicity 
483      *    or <code>null</code> if this multiple set is empty. 
484      * @see #isEmpty
485      */
486     public final T getObjWithMaxMult() {
487 	if (isEmpty()) {
488 	    return null;
489 	}
490 	return getMaxObjWithMult().getKey();
491     }
492 
493     /**
494      * Returns the maximal multiplicity of an element in this set. 
495      * In particular for empty sets returns <code>0</code>. 
496      *
497      * @return 
498      *    a non-negative <code>int</code> value 
499      *    which is the maximal mutliplicity of an element in this set. 
500      *    In particular this is <code>0</code> 
501      *    if and only if this set is empty. 
502      */
503     public final int getMaxMult() {
504 	if (isEmpty()) {
505 	    return 0;
506 	}
507 	return getMaxObjWithMult().getValue().get();
508     }
509 
510     /**
511      * Returns the multiplicity 
512      * with which the given object occurs within this set. 
513      *
514      * @param obj 
515      *    an <code>Object</code> and not null. 
516      * @return 
517      *    a non-negative <code>int</code> value 
518      *    which is the mutliplicity of the given element in this set. 
519      *    In particular this is <code>0</code> if and only if 
520      *    <code>obj</code> is an instance which is not in this set. 
521      * @throws NullPointerException
522      *    for <code>obj==null</code>. 
523      * @see #setMultiplicity(Object, int)
524      * @see #getMultiplicityObj(Object)
525      */
526     public final int getMultiplicity(Object obj) {
527 	// throws NullPointerException for obj==null 
528 	Multiplicity result = getMultiplicityObj(obj);
529 	return result == null ? 0 : result.get();
530     }
531 
532     /**
533      * Returns the multiplicity object of the given object in this set 
534      * or <code>null</code>. 
535      *
536      * @param obj 
537      *    an <code>Object</code> and not null. 
538      * @return 
539      *    If <code>obj</code> is an instance which is in this set, 
540      *    a multiplicity object wrapping the multiplicity is returned. 
541      *    If <code>obj</code> is an instance which is not in this set, 
542      *    <code>null</code> is returned. 
543      * @throws NullPointerException
544      *    for <code>obj==null</code>. 
545      * @see #getMultiplicity(Object)
546      */
547     public final Multiplicity getMultiplicityObj(Object obj) {
548 	if (obj == null) {
549 	    throw new NullPointerException(); // NOPMD 
550 	}
551 	// Here, obj != null. 
552 	return this.obj2mult.get(obj);
553     }
554 
555     /**
556      * Returns <tt>true</tt> if this <code>MultiSet</code> 
557      * contains the specified element. 
558      * More formally, returns <tt>true</tt> if and only if this
559      * <code>MultiSet</code> contains at least one element <tt>e</tt> 
560      * such that <tt>(o==null ? e==null : o.equals(e))</tt>. 
561      *
562      * @param obj 
563      *    element (not <code>null</code>) 
564      *    whose presence in this <code>MultiSet</code> is to be tested.
565      * @return 
566      *    <tt>true</tt> if this <code>MultiSet</code> 
567      *    contains the specified element. 
568      * @throws NullPointerException
569      *    for <code>obj==null</code>. 
570      */
571     public final boolean contains(Object obj) {
572 	// throws NullPointerException for obj==null 
573 	return getMultiplicityObj(obj) != null;
574     }
575 
576     /**
577      * Returns an iterator over the elements in this collection 
578      * which emits each element exactly once, 
579      * without regarding its multiplicity. 
580      * <!-- There are no guarantees concerning the order 
581      * in which the elements are returned
582      * (unless this collection is an instance of some class 
583      * that provides a guarantee). -->
584      * For certain implementations, the iterator returned 
585      * does not allow modifications of the underlying (multi-)set. 
586      * 
587      * @return 
588      *    an <tt>Iterator</tt> over the elements in this collection 
589      *    considering each element exactly once ignoring its multiplicity. 
590      */
591     public final MultiSetIterator<T> iterator() {
592 	return new MultiSetIteratorImpl<T>(this);
593     }
594 
595     /**
596      * Returns an array containing all of the elements 
597      * in this <code>MultiSet</code> exactly once, ignoring its multiplicity. 
598      * <!--
599      * If the <code>MultiSet</code> makes any guarantees 
600      * as to what order its elements are returned by its iterator, 
601      * this method must return the elements in the same order. 
602      * -->
603      * <p>
604      * The returned array will be "safe" in that no references to it 
605      * are maintained by this <code>MultiSet</code>. 
606      * (In other words, this method must allocate a new array 
607      * even if this <code>MultiSet</code> is backed by an array). 
608      * The caller is thus free to modify the returned array. 
609      * <!-- <p>
610      * This method acts as bridge 
611      * between array-based and collection-based APIs. -->
612      *
613      * @return 
614      *    an array containing all of the elements in this collection 
615      * @see #iterator
616      */
617     public final Object[] toArray() {
618 	return getSet().toArray(new Object[0]);
619     }
620 
621     /**
622      * Returns an array containing all of the elements 
623      * in this <code>MultiSet</code>; 
624      * the runtime type of the returned array is that of the specified array. 
625      * If the <code>MultiSet</code> fits in the specified array, 
626      * it is returned therein. 
627      * Otherwise, a new array is allocated with the runtime type 
628      * of the specified array and the size of this <code>MultiSet</code>. 
629      * <p>
630      * If this <code>MultiSet</code> fits in the specified array 
631      * with room to spare
632      * (i.e., the array has more elements than this <code>MultiSet</code>), 
633      * the elementin the array 
634      * immediately following the end of the <code>MultiSet</code> 
635      * is set to <tt>null</tt>. 
636      * This is useful in determining the length of this
637      * <code>MultiSet</code> because this <code>MultiSet</code> does
638      * not contain any <tt>null</tt> elements. 
639      * <p>
640      * <!--
641      * If this <code>MultiSet</code> makes any guarantees 
642      * as to what order its elements are returned by its iterator, 
643      * this method must return the elements in
644      * the same order. 
645      * -->
646      * <!--
647      * <p>
648      * Like the <tt>toArray</tt> method, this method acts as bridge between
649      * array-based and collection-based APIs.  Further, this method allows
650      * precise control over the runtime type of the output array, and may,
651      * under certain circumstances, be used to save allocation costs. 
652      * <p>
653      * -->
654      * Suppose <tt>l</tt> is a <tt>List</tt> known to contain only strings.
655      * The following code can be used to dump the list into a newly allocated
656      * array of <tt>String</tt>:
657      *
658      * <pre>
659      *     String[] x = (String[]) v.toArray(new String[0]);
660      * </pre><p>
661      *
662      * Note that <tt>toArray(new Object[0])</tt> is identical in function to
663      * <tt>toArray()</tt>.
664      *
665      * @param arr 
666      *    the array into which the elements of this <code>MultiSet</code> 
667      *    are to be stored, if it is big enough; 
668      *    otherwise, a new array of the same runtime type 
669      *    is allocated for this purpose. 
670      * @return 
671      *    an array containing each elements of this <code>MultiSet</code> 
672      *    exactly once. 
673      * @throws ArrayStoreException 
674      *    the runtime type of the specified array is not a supertype 
675      *    of the runtime type of every element in this <code>MultiSet</code>. 
676      * @throws NullPointerException 
677      *    if the specified array is <tt>null</tt>. 
678      */
679     public final T[] toArray(T[] arr) {
680 	return getSet().toArray(arr);
681     }
682 
683 
684     // Modification Operations
685 
686     /**
687      * Adds <code>obj</code> to this <code>MultiSet</code> 
688      * and returns the new multiplicity of this object. 
689      * In other words, increments the multiplicity of <code>obj</code> by one. 
690      *
691      * @param obj 
692      *    a <code>Object</code>. 
693      *    Note that this object may not be <code>null</code>. 
694      * @return 
695      *    a strictly positive <code>int</code> value: 
696      *    the new multiplicity of <code>obj</code>. 
697      * @throws NullPointerException 
698      *    if the specified element is null. 
699      * @throws UnsupportedOperationException
700      *    if this <code>MultiSet</code> does not support this method. 
701      */
702     public final int addWithMult(T obj) {
703 	// throws NullPointerException for obj==null 
704 	return addWithMult(obj, 1);
705     }
706 
707     /**
708      * Increases the multiplicity of <code>obj</code> 
709      * in this <code>MultiSet</code> 
710      * by the specified value <code>addMult</code> 
711      * and returns the new multiplicity of this object. 
712      *
713      * @param obj 
714      *    an <code>Object</code> instance. 
715      * @param addMult 
716      *    a non-negative integer specifying the multiplicity 
717      *    with which <code>obj</code> is to be added. 
718      * @return 
719      *    a non-negative <code>int</code> value: 
720      *    the new multiplicity of <code>obj</code>. 
721      * @throws IllegalArgumentException 
722      *    for <code>addMult &lt; 0</code>. 
723      * @throws NullPointerException 
724      *    for <code>obj==null</code> provided <code>addMult &ge; 0</code>. 
725      * @throws UnsupportedOperationException
726      *    if this <code>MultiSet</code> does not support this method. 
727      */
728     public final int addWithMult(T obj, int addMult) {
729 
730 	if (addMult < 0) {
731 	    throw new IllegalArgumentException
732 		("Expected non-negative multiplicity; found " + 
733 		 addMult + ". ");
734 	}
735 	// throws NullPointerException for obj==null 
736 	Multiplicity mult = getMultiplicityObj(obj);
737 
738 	if (mult == null) {
739 	    // Here, this element is not in this set 
740 	    if (addMult != 0) {
741 		assert addMult > 0;
742 		mult = MultiplicityImpl.create(addMult);
743 		this.obj2mult.put(obj, mult);
744 	    }
745 	    return addMult;
746 	}
747 	// Here, obj is already in this set. 
748 	return mult.add(addMult);
749     }
750 
751     /**
752      * Adds <code>obj</code> to this <code>MultiSet</code>. 
753      * In other words, increments the multiplicity of <code>obj</code> by one. 
754      * Returns <tt>true</tt> if this <code>MultiSet</code> 
755      * interpreted as a set changed as a result of the call. 
756      * (Returns <tt>false</tt> if this <code>MultiSet</code> 
757      * already contains the specified element (with nontrivial multiplicity). 
758      * <!--
759      * <p>
760      * <Code>MultiSet</Code>s that support this operation 
761      * may place limitations on what elements may be added 
762      * to this <code>MultiSet</code>. 
763      * In particular, some<code>MultiSet</code>s will refuse 
764      * to add <tt>null</tt> elements, and others will impose restrictions 
765      * on the type of elements that may be added. 
766      * <Code>MultiSet</Code> classes should clearly specify 
767      * in their documentation any 
768      * restrictions on what elements may be added. 
769      * <p>
770      * If a <code>MultiSet</code> refuses to add a particular element 
771      * for any reason other than that it already contains the element, 
772      * it <i>must</i> throw an exception 
773      * (rather than returning <tt>false</tt>).  
774      * This preservesthe invariant 
775      * that a <code>MultiSet</code> always contains the specified element 
776      * after this call returns. 
777      * -->
778      *
779      * @param obj 
780      *    element the multiplicity of which in this <code>MultiSet</code> 
781      *    is to be increased by one. 
782      *    Note that this may not be <code>null</code>. 
783      * @return 
784      *    <tt>true</tt> if and only if 
785      *    the multiplicity of the specified element 
786      *    was <code>0</code> before the call of this method. 
787      * @throws NullPointerException 
788      *    if the specified element is <code>null</code>. 
789      * @throws UnsupportedOperationException
790      *    if this <code>MultiSet</code> does not support this method. 
791      */
792     public final boolean add(T obj) {
793 	// throws NullPointerException for obj==null 
794 	Multiplicity mult = getMultiplicityObj(obj);
795 	if (mult == null) {
796 	    mult = MultiplicityImpl.create(1);
797 	    this.obj2mult.put(obj, mult);
798 	    return true;
799 	}
800 	mult.add(1);
801 	return false;
802    }
803 
804     /**
805      * Decrements the multiplicity of <code>obj</code> 
806      * in this <code>MultiSet</code> if it is present and 
807      * returns the <em>old</em> multiplicity of <code>obj</code>; 
808      * If this is <code>0</code> returns 
809      * without altering this <code>MultiSet</code>. 
810      *
811      * @param obj 
812      *    a <code>Object</code>. 
813      *    Note that this object may not be <code>null</code>. 
814      * @return 
815      *    a non-negative <code>int</code> value: 
816      *    the old multiplicity of <code>obj</code> 
817      *    before a potential modification of this <code>MultiSet</code>. 
818      * @throws NullPointerException 
819      *    if the specified element is null. 
820      * @throws UnsupportedOperationException
821      *    if this <code>MultiSet</code> does not support this method. 
822      */
823     public final int removeWithMult(Object obj) {
824 	// throws NullPointerException for obj==null 
825 	return removeWithMult(obj, 1);
826    }
827 
828     /**
829      * Decreases the multiplicity of <code>obj</code> 
830      * in this <code>MultiSet</code> 
831      * by the specified value <code>removeMult</code> if possible 
832      * and returns the <em>old</em> multiplicity of <code>obj</code>. 
833      *
834      * @param obj 
835      *    an <code>Object</code> instance. 
836      * @param removeMult 
837      *    a non-negative integer specifying the multiplicity 
838      *    with which <code>obj</code> is to be removed. 
839      * @return 
840      *    a non-negative <code>int</code> value: 
841      *    the old multiplicity of <code>obj</code> 
842      *    before a potential modification of this <code>MultiSet</code>. 
843      * @throws NullPointerException 
844      *    for <code>obj == null</code>. 
845      * @throws IllegalArgumentException 
846      *    for <code>removeMult &lt; 0</code> and also if 
847      *    <code>removeMult - obj.getMultiplicity() &lt; 0</code>. 
848      * @throws UnsupportedOperationException
849      *    if this <code>MultiSet</code> does not support this method. 
850      */
851     public final int removeWithMult(Object obj, int removeMult) {
852 	if (removeMult < 0) {
853 	    throw new IllegalArgumentException
854 		("Expected non-negative multiplicity; found " + 
855 		 removeMult + ". ");
856 	}
857 
858 	// throws NullPointerException for obj==null 
859 	Multiplicity mult = getMultiplicityObj(obj);
860 	if (mult == null) {
861 	    if (removeMult != 0) {
862 		throw new IllegalArgumentException
863 		    ("Tried to remove object " + obj + 
864 		     " which is not in this MultiSet. ");
865 	    }
866 	    return 0;
867 	}
868 	// return value is old multiplicity 
869 	int ret = mult.get();
870 	if (ret == removeMult) {
871 	    this.obj2mult.remove(obj);
872 	} else {
873 	    mult.add(-removeMult);
874 	}
875 	return ret;
876     }
877 
878     /**
879      * Removes <em>all</em> instances of the specified element from this 
880      * <code>MultiSet</code>, if it is present with nontrivial multiplicity. 
881      * More formally,
882      * immediately after having (successively) 
883      * invoked <code>s.remove(o)</code>, 
884      * the condition <code>s.contains(o) == false</code> is satisfied. 
885      * Returns true if this <code>MultiSet</code> contained the specified 
886      * element (or equivalently, if (the underlying set of) 
887      * this <code>MultiSet</code> changed as a result of the call). 
888      *
889      * @param obj 
890      *    element the multiplicity of which in this <code>MultiSet</code> 
891      *    is to be increased by one. 
892      * @return 
893      *    <tt>true</tt> if and only if this <code>MultiSet</code> changed 
894      *    as a result of the call. 
895      * @throws NullPointerException 
896      *    if the specified element is <code>null</code>. 
897      * @throws UnsupportedOperationException
898      *    if this <code>MultiSet</code> does not support this method. 
899      */
900     public final boolean remove(Object obj) {
901 	if (obj == null) {
902 	    throw new NullPointerException(); // NOPMD
903 	}
904 	// Here, obj != null. 
905 
906 	return this.obj2mult.remove(obj) != null;
907     }
908 
909     /**
910      * Sets the multiplicity of <code>obj</code> to the value 
911      * specified by <code>mult</code>. 
912      *
913      * @param obj 
914      *    an <code>Object</code> instance. 
915      * @param newMult 
916      *    a non-negative <code>int</code> value. 
917      * @return 
918      *    the old multiplicity of <code>obj</code> 
919      *    as a non-negative <code>int</code> value. 
920      * @throws IllegalArgumentException 
921      *   if either <code>obj == null</code> or <code>mult &le; 0</code>. 
922      * @throws UnsupportedOperationException
923      *    if this <code>MultiSet</code> does not support this method. 
924      * @see #getMultiplicity(Object)
925      */
926     public final int setMultiplicity(T obj, int newMult) {
927 	if (obj == null) {
928 	    throw new IllegalArgumentException
929 		("Found null element. ");
930 	}
931 	if (newMult < 0) {
932 	    throw new IllegalArgumentException
933 		("Found negative multiplicity " + newMult + ". ");
934 	}
935 
936 	Multiplicity oldMult = newMult == 0 
937 	    ? this.obj2mult.remove(obj)
938 	    : this.obj2mult.put(obj, MultiplicityImpl.create(newMult));
939 	return oldMult == null ? 0 : oldMult.get();
940     }
941 
942     // Bulk Operations
943 
944     /**
945      * Returns <tt>true</tt> if this <code>MultiSet</code> 
946      * contains all of the elements in the specified collection 
947      * with strictly positive multiplicity. 
948      *
949      * @param  coll 
950      *    collection to be checked for containment 
951      *    in this <code>MultiSet</code>.
952      * @return 
953      *    <tt>true</tt> if this <code>MultiSet</code> 
954      *    contains all of the elements in the specified collection. 
955      * @throws NullPointerException 
956      *    if the specified collection contains one or more null elements.
957      * @throws NullPointerException 
958      *    if the specified collection is <tt>null</tt>.
959      * @see #contains(Object)
960      */
961     public final boolean containsAll(Collection<?> coll) {
962 	for (Object cand : coll) {
963 	    // throws NullPointerException if cand == null
964 	    if (!contains(cand)) {
965 		return false;
966 	    }
967 	}
968 	return true;
969     }
970 
971     /**
972      * Adds <code>mvs</code> elementwise to this multi set 
973      * increasing multiplicities 
974      * and returns whether this caused a change 
975      * of the underlying set. 
976      * **** strange implementation; also: change 
977      *
978      * @param mvs 
979      *    a <code>MultiSet</code> object. 
980      * @return 
981      *    returns whether adding changed this <code>MultiSet</code> 
982      *    interpreted as a set. 
983      * @throws UnsupportedOperationException
984      *    if this <code>MultiSet</code> does not support this method. 
985      */
986     public final boolean addAll(MultiSet<? extends T> mvs) {
987 
988 	int mvsMult;
989 	boolean added = false;
990 	for (T cand : mvs.getSet()) {
991 	    mvsMult = mvs.getMultiplicity(cand);
992 	    assert mvsMult > 0;
993 	    Multiplicity mult = this.obj2mult.get(cand);
994 	    if (mult == null) {
995 		// Here, cand has not been in this multi-set 
996 		this.obj2mult.put(cand, MultiplicityImpl.create(mvsMult));
997 		added = true;
998 	    } else {
999 		// Here, cand has been in this multi-set 
1000 		mult.add(mvsMult);
1001 	    }
1002 	} // for 
1003 	return added;
1004     }
1005 
1006     /**
1007      * Adds <code>set</code> elementwise to this multi set 
1008      * increasing multiplicities 
1009      * and returns whether this caused a change 
1010      * of the underlying set. 
1011      * **** strange implementation; also: change 
1012      *
1013      * @param set 
1014      *    a <code>Set</code> object. 
1015      * @return 
1016      *    returns whether adding changed this <code>MultiSet</code> 
1017      *    interpreted as a set. 
1018      * @throws UnsupportedOperationException
1019      *    if this <code>MultiSet</code> does not support this method. 
1020      */
1021     public final boolean addAll(Set<? extends T> set) {
1022 	boolean added = false;
1023 	for (T cand : set) {
1024 	    Multiplicity mult = this.obj2mult.get(cand);
1025 	    if (mult == null) {
1026 		// Here, cand has not been in this multi-set 
1027 		this.obj2mult.put(cand, MultiplicityImpl.create(1));
1028 		added = true;
1029 	    } else {
1030 		// Here, cand has been in this multi-set 
1031 		mult.add(1);
1032 	    }
1033 	} // for 
1034 	return added;
1035     }
1036 
1037     /**
1038      * Removes all this <code>MultiSet</code>'s elements 
1039      * that are also contained in the specified collection. 
1040      * After this call returns, this <code>MultiSet</code> 
1041      * will contain no elements in common with the specified collection. 
1042      *
1043      * @param coll 
1044      *    elements to be removed from this <code>MultiSet</code>. 
1045      * @return 
1046      *    <tt>true</tt> if this <code>MultiSet</code> 
1047      *    changed as a result of the call. 
1048      * @throws NullPointerException 
1049      *    if the specified collection is <code>null</code>. 
1050      * @throws UnsupportedOperationException
1051      *    if this <code>MultiSet</code> does not support this method. 
1052      * @see #remove(Object)
1053      * @see #contains(Object)
1054      */
1055     public final boolean removeAll(Collection<?> coll) {
1056 	boolean thisChanged = false;
1057 	for (Object cand : coll) {
1058 	    // throws NullPointerException if cand == null 
1059 	    thisChanged |= remove(cand);
1060 	}
1061 	return thisChanged;
1062     }
1063 
1064     /**
1065      * Retains only the elements in this <code>MultiSet</code> 
1066      * that are contained in the specified collection. 
1067      * In other words, removes from this <code>MultiSet</code> 
1068      * all of its elements that are not contained 
1069      * in the specified collection. 
1070      *
1071      * @param coll 
1072      *    elements to be retained in this <code>MultiSet</code>. 
1073      * @return 
1074      *    <tt>true</tt> if this <code>MultiSet</code> changed 
1075      *    as a result of the call. 
1076      * @throws NullPointerException 
1077      *    if the specified collection is <tt>null</tt>.
1078      * @throws UnsupportedOperationException
1079      *    if this <code>MultiSet</code> does not support this method. 
1080      * @see #remove(Object)
1081      * @see #contains(Object)
1082      */
1083     public final boolean retainAll(Collection<?> coll) {
1084 	boolean result = false;
1085 
1086 	Iterator<T> iter = iterator();
1087 	T cand;
1088 	while (iter.hasNext()) {
1089 	    cand = iter.next();
1090 	    if (!coll.contains(cand)) {
1091 		iter.remove();
1092 		result = true;
1093 	    }
1094 	}
1095 	return result;
1096     }
1097 
1098     /**
1099      * Removes all of the elements from this <code>MultiSet</code>. 
1100      * This <code>MultiSet</code> will be empty after this method returns. 
1101      *
1102      * @throws UnsupportedOperationException
1103      *    if this <code>MultiSet</code> does not support this method. 
1104      */
1105     public final void clear() {
1106 	this.obj2mult.clear();
1107     }
1108 
1109     /**
1110      * Returns a Set view of the mapping 
1111      * from the element of this <code>MultiSet</code> 
1112      * to the according multiplicities. 
1113      * The set is backed by the <code>MultiSet</code>, 
1114      * so changes to the map are reflected in the set, and vice-versa. 
1115      * If the <code>MultiSet</code> is modified 
1116      * while an iteration over the set is in progress 
1117      * (except through the iterator's own remove operation, 
1118      * or through the setValue operation on a map entry 
1119      * returned by the iterator) the results of the iteration are undefined. 
1120      * The set may support element removal, 
1121      * which removes the corresponding element from the <code>MultiSet</code>, 
1122      * via the {@link Iterator#remove()}, {@link Set#remove(Object)}, 
1123      * {@link Set#removeAll(Collection)}, {@link Set#retainAll(Collection)} 
1124      * and {@link #clear()} operations. 
1125      * It does not support the methods 
1126      * {@link #add(Object)} or {@link Set#addAll(Collection)}. 
1127      */
1128     public final Set<Map.Entry<T, Multiplicity>> getSetWithMults() {
1129 	return this.obj2mult.entrySet();
1130     }
1131 
1132     /**
1133      * Returns <code>true</code> if and only if <code>obj</code> 
1134      * is also a <code>MultiSet</code> 
1135      * and contains the same elements with the same multiplicities 
1136      * as this one. 
1137      *
1138      * @param obj 
1139      *    an <code>Object</code>, possibly <code>null</code>. 
1140      * @return 
1141      *    a <code>true</code> if and only if <code>obj</code> 
1142      *    is also a <code>MultiSet</code> 
1143      *    and contains the same elements with the same multiplicities 
1144      *    as this one. 
1145      */
1146     public final boolean equals(Object obj) {
1147 	if (!(obj instanceof MultiSet)) {
1148 	    return false;
1149 	}
1150 	MultiSet<?> other = (MultiSet) obj;
1151 	return this.getSetWithMults().equals(other.getSetWithMults());
1152     }
1153 
1154     public final int hashCode() {
1155 	int result = 0;
1156 	for (T cand : getSet()) {
1157 	    result += cand.hashCode() * getMultiplicity(cand);
1158 	}
1159 	return result;
1160     }
1161 }