/*******************************************************************************
 * 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
 *******************************************************************************/

package org.eclipse.ui.navigator;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.jface.viewers.AbstractTreeViewer;

/**
 *
 * A pipelined viewer update should map requests to refresh or update elements
 * in the viewer to their correct, modified structure. Clients use
 * {@link PipelinedViewerUpdate} as the input and return type from intercept
 * methods on {@link IPipelinedTreeContentProvider}.
 *
 * <p>
 * Clients should use the viewer update to describe how the request from the
 * upstream extension (see {@link IPipelinedTreeContentProvider} for more
 * information on <i>upstream</i> extensions) should be reshaped when applied
 * to the tree. A request from an upstream extension to refresh a given element
 * could result in multiple refresh requests from downstream extensions.
 * Therefore, the refresh targets are modeled as a set.
 * </p>
 * <p>
 * Initially, this set will contain the original element that was passed to the
 * refresh requests. Clients may squash the refresh by clearing the set, change
 * the original target by removing the current element and adding a new target,
 * or expand the refresh by adding more elements to the set.
 * </p>
 * <p>
 * A pipelined extension may receive a {@link PipelinedViewerUpdate} as the
 * result of a call to {@link AbstractTreeViewer#refresh()}-methods or
 * {@link AbstractTreeViewer#update(Object, String[])}-methods. The
 * <code>properties</code> field is only applicable for <code>update()</code>
 * calls and the <code>updateLabels</code> field is only applicable for
 * <code>refresh()</code> calls.
 * </p>
 *
 * @since 3.2
 *
 */
public final class PipelinedViewerUpdate {

	private static final String[] NO_PROPERTIES = new String[0];

	private final Set refreshTargets = new LinkedHashSet();

	private boolean updateLabels = false;

	private Map properties;

	/**
	 * Properties allow optimization for <code>update</code> calls.
	 *
	 * @param aTarget
	 *            The target which may have specific properties associated with
	 *            it for an optimized refresh.
	 *
	 * @return Returns the properties for the given target. If no properties are
	 *         specified, then an empty array is returned. <b>null</b> will
	 *         never be returned.
	 */
	public final String[] getProperties(Object aTarget) {
		if (properties != null && properties.containsKey(aTarget)) {
			String[] props = (String[]) properties.get(aTarget);
			return props != null ? props : NO_PROPERTIES;
		}
		return NO_PROPERTIES;
	}

	/**
	 *
	 * Properties allow optimization for <code>update</code> calls.
	 *
	 * @param aTarget
	 *            The target of the properties.
	 * @param theProperties
	 *            The properties to pass along to the <code>update</code>
	 *            call.
	 * @see AbstractTreeViewer#update(Object, String[])
	 */
	public final void setProperties(Object aTarget, String[] theProperties) {
		if (theProperties != null && theProperties.length > 0) {
			if (properties == null) {
				properties = new HashMap();
			}
			properties.put(aTarget, theProperties);

		} else {
			properties.remove(aTarget);
		}

		if (properties.isEmpty()) {
			properties = null;
		}

	}

	/**
	 * @return Returns the current set of refresh targets. Clients may add or
	 *         remove directly to or from this set.
	 */
	public final Set getRefreshTargets() {
		return refreshTargets;
	}

	/**
	 * @return Returns the true if the labels should also be updated during the
	 *         <code>refresh</code>.
	 */
	public final boolean isUpdateLabels() {
		return updateLabels;
	}

	/**
	 * @param toUpdateLabels
	 *            True indicates that calls to <code>refresh</code> should
	 *            force the update of the labels in addition to refreshing the
	 *            structure.
	 */
	public final void setUpdateLabels(boolean toUpdateLabels) {
		updateLabels = toUpdateLabels;
	}

}
