/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A ReferenceCounter is used to reference counting objects.
 * Each object is identified by a unique ID.  Together they form
 * an ID - value pair. An object is added to the counter by calling
 * #put(id, object).  From this point on additional refs can be made
 * by calling #addRef(id) or #removeRef(id).
 */
public class ReferenceCounter {
    private Map mapIdToRec = new HashMap(11);

    /**
     * Capture the information about an object.
     */
    public static class RefRec {
        public RefRec(Object id, Object value) {
            this.id = id;
            this.value = value;
            addRef();
        }

        public Object getId() {
            return id;
        }

        public Object getValue() {
            return value;
        }

        public int addRef() {
            ++refCount;
            return refCount;
        }

        public int removeRef() {
            --refCount;
            return refCount;
        }

        public int getRef() {
            return refCount;
        }

        public boolean isNotReferenced() {
            return (refCount <= 0);
        }

        public Object id;

        public Object value;

        private int refCount;
    }

    /**
     * Creates a new counter.
     */
    public ReferenceCounter() {
        super();
    }

    /**
     * Adds one reference to an object in the counter.
     *
     * @param id is a unique ID for the object.
     * @return the new ref count
     */
    public int addRef(Object id) {
        RefRec rec = (RefRec) mapIdToRec.get(id);
        if (rec == null) {
			return 0;
		}
        return rec.addRef();
    }

    /**
     * Returns the object defined by an ID.  If the ID is not
     * found <code>null</code> is returned.
     *
     * @return the object or <code>null</code>
     */
    public Object get(Object id) {
        RefRec rec = (RefRec) mapIdToRec.get(id);
        if (rec == null) {
			return null;
		}
        return rec.getValue();
    }

    /**
     * Returns a complete list of the keys in the counter.
     *
     * @return a Set containing the ID for each.
     */
    public Set keySet() {
        return mapIdToRec.keySet();
    }

    /**
     * Adds an object to the counter for counting and gives
     * it an initial ref count of 1.
     *
     * @param id is a unique ID for the object.
     * @param value is the object itself.
     */
    public void put(Object id, Object value) {
        RefRec rec = new RefRec(id, value);
        mapIdToRec.put(id, rec);
    }

    /**
     * @param id is a unique ID for the object.
     * @return the current ref count
     */
    public int getRef(Object id) {
        RefRec rec = (RefRec) mapIdToRec.get(id);
        if (rec == null) {
			return 0;
		}
        return rec.refCount;
    }

    /**
     * Removes one reference from an object in the counter.
     * If the ref count drops to 0 the object is removed from
     * the counter completely.
     *
     * @param id is a unique ID for the object.
     * @return the new ref count
     */
    public int removeRef(Object id) {
    	RefRec rec = (RefRec) mapIdToRec.get(id);
    	if (rec == null) {
    		return 0;
    	}
    	int newCount = rec.removeRef();
    	if (newCount <= 0) {
    		mapIdToRec.remove(id);
    	}
    	return newCount;
    }

    /**
     * Returns a complete list of the values in the counter.
     *
     * @return a Collection containing the values.
     */
    public List values() {
        int size = mapIdToRec.size();
        ArrayList list = new ArrayList(size);
        Iterator iter = mapIdToRec.values().iterator();
        while (iter.hasNext()) {
            RefRec rec = (RefRec) iter.next();
            list.add(rec.getValue());
        }
        return list;
    }
}
