/*******************************************************************************
 * Copyright (c) 2006, 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
 *     C. Sean Young <csyoung@google.com> - Bug 436645
 ******************************************************************************/
package org.eclipse.ui.internal.navigator.extensions;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

/**
 * A reference meant to be a key for an evaluation cache.
 *
 * Should only be used to point to objects that are immutable.
 *
 * @param <T>
 *            The type of object this reference points to.
 *
 * @since 3.3
 */
public class EvaluationReference<T> extends SoftReference<T> {
	private final int hashCode;

	/**
	 * @param referent
	 *            The object to be referenced, must be non-null
	 */
	public EvaluationReference(T referent) {
		super(referent);
		hashCode = referent.hashCode();
	}

	/**
	 *
	 * @param referent
	 *            The object to be referenced, must be non-null
	 * @param queue
	 *            The ReferenceQueue to register this instance in
	 */
	public EvaluationReference(T referent, ReferenceQueue<? super T> queue) {
		super(referent, queue);
		hashCode = referent.hashCode();
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	/**
	 * Returns true if the Object given is also an EvaluationReference and if the
	 * referents are equal, or if the referent is null (implying clearing or collection),
	 * if the given Object is exactly this EvaluationReference object.
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		} else if (obj == this) {
			return true;
		} else if (obj instanceof EvaluationReference) {
			// Don't get if not given an evaluation reference to prevent
			// unnecessary accesses keeping the SoftReference "fresh".
			Object myObj = get();
			// If the inner object is null, then the only EvaluationReference
			// that is equal is itself (checked above).
			if (myObj == null) return false;
			EvaluationReference<?> otherRef = (EvaluationReference<?>) obj;
			if (hashCode != otherRef.hashCode) return false;
			// Not comparing type; it is valid for two objects of different
			// types to be equal.
			Object otherObj = otherRef.get();
			if (otherObj == null) return false;
			return myObj == otherObj || myObj.equals(otherObj);
		}
		return false;
	}

	@Override
	public String toString() {
		Object referent = get();
		return "Evalutation[" + (referent == null ? "(collected)" : "referent=" + referent) + ']'; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}
}
