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

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Arrays;

/**
 * A reference meant to be a value object for an EvaluationReference key type;
 * weakly holds an {@link EvaluationReference} (the reference itself, not the
 * underlying object) representing this value's key. Used to support
 * ReferenceQueue based cleanups in maps.
 *
 * Does not override equals or hashCode; uses identity comparison inherited from
 * Object.
 *
 * @param <T>
 *            The type of object this reference points to.
 *
 * @see EvaluationReference
 * @since 3.3
 */
public class EvaluationValueReference<T> extends SoftReference<T> {
	private Reference<EvaluationReference<?>> refToKey;

	/**
	 * @param referrent
	 *            The object to be referenced
	 * @param key
	 *            The key this value is associated with
	 */
	public EvaluationValueReference(T referrent, EvaluationReference<?> key) {
		super(referrent);
		this.refToKey = new WeakReference<>(key);
	}

	/**
	 * @param referrent
	 *            The object to be referenced
	 * @param key
	 *            The key this value is associated with
	 * @param queue
	 *            The ReferenceQueue to register this instance in
	 */
	public EvaluationValueReference(T referrent, EvaluationReference<?> key,
			ReferenceQueue<? super T> queue) {
		super(referrent, queue);
		this.refToKey = new WeakReference<>(key);
	}

	/**
	 * @return the key that this value was associated with, or null if this
	 *         value has been cleared or the key has been collected.
	 */
	public EvaluationReference<?> getKey() {
		return refToKey.get();
	}

	/**
	 * Facilitates "handing off" a particular instance of a key.
	 *
	 * @param otherValue
	 *            the value ref to copy the key from.
	 */
	void swapKey(EvaluationValueReference<?> otherValue) {
		Reference<EvaluationReference<?>> tmp = refToKey;
		this.refToKey = otherValue.refToKey;
		otherValue.refToKey = tmp;
	}

	/**
	 * Clears this reference and the underlying reference to the key.
	 *
	 * @see java.lang.ref.Reference#clear()
	 */
	@Override
	public void clear() {
		super.clear();
		// This only clears our reference to the key, not the key itself.
		refToKey.clear();
	}

	private static String toStringArrayAware(Object o) {
		// Yea, this will miss primitive arrays, but nothing is using this for those yet.
		if (o instanceof Object[]) {
			return Arrays.toString((Object[]) o);
		}
		return String.valueOf(o);
	}

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