/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.collections4;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

/**
 * Defines a collection that counts the number of times an object appears in
 * the collection.
 * <p>
 * Suppose you have a Bag that contains {@code {a, a, b, c}}.
 * Calling {@link #getCount(Object)} on {@code a} would return 2, while
 * calling {@link #uniqueSet()} would return {@code {a, b, c}}.
 * </p>
 * <p>
 * <em>NOTE: This interface violates the {@link Collection} contract.</em>
 * The behavior specified in many of these methods is <em>not</em> the same
 * as the behavior specified by {@code Collection}.
 * The non-compliant methods are clearly marked with "(Violation)".
 * Exercise caution when using a bag as a {@code Collection}.
 * </p>
 * <p>
 * This violation resulted from the original specification of this interface.
 * In an ideal world, the interface would be changed to fix the problems, however
 * it has been decided to maintain backwards compatibility instead.
 * </p>
 * <p>
 * The {@link MultiSet} interface, added in version 4.1, provides the same
 * functionality while complying with the {@link Collection} contract, and
 * should be preferred for new code. Existing code can migrate as follows,
 * with the cardinality-respecting behavior preserved through explicitly
 * named methods:
 * </p>
 * <ul>
 *   <li>{@code bag.add(e)} becomes {@code multiSet.add(e)}
 *       (which always returns {@code true}, per the {@code MultiSet} contract)</li>
 *   <li>{@code bag.getCount(e)} becomes {@code multiSet.getCount(e)}</li>
 *   <li>{@code bag.remove(e)}, which removes all copies, becomes
 *       {@code multiSet.setCount(e, 0)}</li>
 *   <li>{@code bag.containsAll(coll)} becomes
 *       {@code MultiSetUtils.containsOccurrences(multiSet, new HashMultiSet<>(coll))}</li>
 *   <li>{@code bag.removeAll(coll)} becomes
 *       {@code MultiSetUtils.removeOccurrences(multiSet, new HashMultiSet<>(coll))}</li>
 *   <li>{@code bag.retainAll(coll)} becomes
 *       {@code MultiSetUtils.retainOccurrences(multiSet, new HashMultiSet<>(coll))}</li>
 *   <li>{@link SortedBag} and {@code TreeBag} become {@link SortedMultiSet}
 *       and {@code TreeMultiSet}</li>
 *   <li>the {@code CollectionBag} and {@code CollectionSortedBag} wrappers are
 *       not needed, as a {@code MultiSet} already complies with the
 *       {@code Collection} contract</li>
 * </ul>
 *
 * @param <E> The type of elements in this bag
 * @see MultiSet
 * @since 2.0
 * @deprecated Since 4.6.0, use {@link MultiSet} instead; see the migration notes above.
 */
@Deprecated
public interface Bag<E> extends Collection<E> {

    /**
     * <em>(Violation)</em>
     * Adds one copy of the specified object to the Bag.
     * <p>
     * If the object is already in the {@link #uniqueSet()} then increment its
     * count as reported by {@link #getCount(Object)}. Otherwise add it to the
     * {@link #uniqueSet()} and report its count as 1.
     * </p>
     * <p>
     * Since this method always increases the size of the bag,
     * according to the {@link Collection#add(Object)} contract, it
     * should always return {@code true}. Since it sometimes returns
     * {@code false}, this method violates the contract.
     * </p>
     *
     * @param object  The object to add.
     * @return {@code true} if the object was not already in the {@code uniqueSet}.
     */
    @Override
    boolean add(E object);

    /**
     * Adds {@code nCopies} copies of the specified object to the Bag.
     * <p>
     * If the object is already in the {@link #uniqueSet()} then increment its
     * count as reported by {@link #getCount(Object)}. Otherwise add it to the
     * {@link #uniqueSet()} and report its count as {@code nCopies}.
     * </p>
     *
     * @param object  The object to add.
     * @param nCopies  The number of copies to add.
     * @return {@code true} if the object was not already in the {@code uniqueSet}.
     * @throws ClassCastException if the class of the specified element prevents it from being added to this collection.
     */
    boolean add(E object, int nCopies);

    /**
     * <em>(Violation)</em>
     * Returns {@code true} if the bag contains all elements in
     * the given collection, respecting cardinality. That is, if the
     * given collection {@code coll} contains {@code n} copies
     * of a given object, calling {@link #getCount(Object)} on that object must
     * be {@code &gt;= n} for all {@code n} in {@code coll}.
     *
     * <p>
     * The {@link Collection#containsAll(Collection)} method specifies
     * that cardinality should <em>not</em> be respected; this method should
     * return true if the bag contains at least one of every object contained
     * in the given collection.
     * </p>
     *
     * @param coll  The collection to check against.
     * @return {@code true} if the Bag contains all the collection.
     */
    @Override
    boolean containsAll(Collection<?> coll);

    /**
     * Gets the number of occurrences (cardinality) of the given
     * object currently in the bag. If the object does not exist in the
     * bag, return 0.
     *
     * @param object  The object to search for.
     * @return The number of occurrences of the object, zero if not found.
     */
    int getCount(Object object);

    /**
     * Returns an {@link Iterator} over the entire set of members,
     * including copies due to cardinality. This iterator is fail-fast
     * and will not tolerate concurrent modifications.
     *
     * @return iterator over all elements in the Bag.
     */
    @Override
    Iterator<E> iterator();

    /**
     * <em>(Violation)</em>
     * Removes all occurrences of the given object from the bag.
     * <p>
     * This will also remove the object from the {@link #uniqueSet()}.
     * </p>
     * <p>
     * According to the {@link Collection#remove(Object)} method,
     * this method should only remove the <em>first</em> occurrence of the
     * given object, not <em>all</em> occurrences.
     * </p>
     *
     * @param object  The object to remove.
     * @return {@code true} if this call changed the collection.
     */
    @Override
    boolean remove(Object object);

    /**
     * Removes {@code nCopies} copies of the specified object from the Bag.
     * <p>
     * If the number of copies to remove is greater than the actual number of
     * copies in the Bag, no error is thrown.
     * </p>
     *
     * @param object  The object to remove.
     * @param nCopies  The number of copies to remove.
     * @return {@code true} if this call changed the collection.
     */
    boolean remove(Object object, int nCopies);

    /**
     * <em>(Violation)</em>
     * Remove all elements represented in the given collection,
     * respecting cardinality. That is, if the given collection
     * {@code coll} contains {@code n} copies of a given object,
     * the bag will have {@code n} fewer copies, assuming the bag
     * had at least {@code n} copies to begin with.
     *
     * <p>
     * The {@link Collection#removeAll(Collection)} method specifies
     * that cardinality should <em>not</em> be respected; this method should
     * remove <em>all</em> occurrences of every object contained in the
     * given collection.
     * </p>
     *
     * @param coll  The collection to remove.
     * @return {@code true} if this call changed the collection.
     */
    @Override
    boolean removeAll(Collection<?> coll);

    /**
     * <em>(Violation)</em>
     * Remove any members of the bag that are not in the given
     * collection, respecting cardinality. That is, if the given
     * collection {@code coll} contains {@code n} copies of a
     * given object and the bag has {@code m > n} copies, then
     * delete {@code m - n} copies from the bag. In addition, if
     * {@code e} is an object in the bag but
     * {@code !coll.contains(e)}, then remove {@code e} and any
     * of its copies.
     *
     * <p>
     * The {@link Collection#retainAll(Collection)} method specifies
     * that cardinality should <em>not</em> be respected; this method should
     * keep <em>all</em> occurrences of every object contained in the
     * given collection.
     * </p>
     *
     * @param coll  The collection to retain.
     * @return {@code true} if this call changed the collection.
     */
    @Override
    boolean retainAll(Collection<?> coll);

    /**
     * Returns the total number of items in the bag across all types.
     *
     * @return The total size of the Bag.
     */
    @Override
    int size();

    /**
     * Returns a {@link Set} of unique elements in the Bag.
     * <p>
     * Uniqueness constraints are the same as those in {@link Set}.
     * </p>
     *
     * @return The Set of unique Bag elements.
     */
    Set<E> uniqueSet();

    // The following is not part of the formal Bag interface, however where possible
    // Bag implementations should follow these comments.
//    /**
//     * Compares this Bag to another.
//     * This Bag equals another Bag if it contains the same number of occurrences of
//     * the same elements.
//     * This equals definition is compatible with the Set interface.
//     *
//     * @param obj  The Bag to compare to
//     * @return true if equal
//     */
//    boolean equals(Object obj);
//
//    /**
//     * Gets a hash code for the Bag compatible with the definition of equals.
//     * The hash code is defined as the sum total of a hash code for each element.
//     * The per element hash code is defined as
//     * {@code (e==null ? 0 : e.hashCode()) ^ noOccurrences)}.
//     * This hash code definition is compatible with the Set interface.
//     *
//     * @return The hash code of the Bag
//     */
//    int hashCode();

}
