/******************************************************************************* * Copyright (c) 2003, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.navigator.extensions; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.StructuredViewerInternals; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Item; import org.eclipse.ui.internal.navigator.NavigatorContentService; import org.eclipse.ui.internal.navigator.NavigatorSafeRunnable; import org.eclipse.ui.internal.navigator.Policy; /** * <p> * Provides a consistent mechanism to interact with StructuredViewers over time. * The Common Navigator framework attempts to defer the loading of extensions, * which also means deferring the loading of Content Providers. To follow the * contracts already in place by * {@link org.eclipse.jface.viewers.ITreeContentProvider}, the Viewer, Old * Input, and New Input parameters for * {@link org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)} * are cached for content providers that have not been loaded yet. * </p> * <p> * <b>WARNING: </b> The following class is not inherently thread-safe. * Appropriate measures should be taken to ensure that * {@link #inputChanged(Object, Object)}and * {@link #inputChanged(Viewer, Object, Object)}are not called concurrently * with {@link #initialize(IStructuredContentProvider)}. * * * * @since 3.2 */ public class StructuredViewerManager { private StructuredViewer viewer; private Object cachedOldInput; private Object cachedNewInput; /* * This map is used to associate elements in the viewer with their * associated NavigatorContentDescriptor. To avoid things getting out of * hand, it associates only the items that are actually present in the tree. * We need this association to make sure that we can always get the source * (NavigatorContentDescriptor) of a given element for the case of providing * the label which must use the same navigator content extension that * provided the element. */ // Map<element, NavigatorContentDescriptor> private Map viewerDataMap; static class StructuredViewerAccess extends StructuredViewerInternals { static class Listener implements StructuredViewerInternals.AssociateListener { private final NavigatorContentService contentService; private final Map viewerDataMap; public Listener(NavigatorContentService contentService, Map viewerDataMap) { this.contentService = contentService; this.viewerDataMap = viewerDataMap; } @Override public void associate(Object element, Item item) { NavigatorContentDescriptor desc = contentService.getContribution(element); contentService.forgetContribution(element); synchronized (viewerDataMap) { if (viewerDataMap.containsKey(element)) { if (Policy.DEBUG_VIEWER_MAP) System.out.println("associate: SKIPPED " + element + " item: " + item + " desc: " + desc + " FOUND"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ return; } viewerDataMap.put(element, desc); if (Policy.DEBUG_VIEWER_MAP) System.out.println("associate: " + element + " item: " + item + " desc: " + desc); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } @Override public void disassociate(Item item) { synchronized (viewerDataMap) { if (Policy.DEBUG_VIEWER_MAP) System.out.println("disassociate: item: " + item + " object: " + item.getData()); //$NON-NLS-1$ //$NON-NLS-2$ viewerDataMap.remove(item.getData()); } } @Override public void filteredOut(Object element) { contentService.forgetContribution(element); synchronized (viewerDataMap) { if (Policy.DEBUG_VIEWER_MAP) System.out.println("filteredOut: object: " + element); //$NON-NLS-1$ viewerDataMap.remove(element); } } } protected static void hookAssociateListener(StructuredViewer v, Map viewerDataMap, NavigatorContentService contentService) { StructuredViewerInternals.setAssociateListener(v, new Listener(contentService, viewerDataMap)); } } /** * @param element * @return the object */ public Object getData(Object element) { synchronized (viewerDataMap) { return viewerDataMap.get(element); } } /** * Used when NCEs associated with the viewer are changed. */ public void resetViewerData() { synchronized (viewerDataMap) { if (Policy.DEBUG_VIEWER_MAP) System.out.println("viewer map RESET"); //$NON-NLS-1$ viewerDataMap.clear(); } } /** * * @param aViewer * @param contentService */ public StructuredViewerManager(StructuredViewer aViewer, NavigatorContentService contentService) { super(); viewer = aViewer; viewerDataMap = new HashMap(); StructuredViewerAccess.hookAssociateListener(viewer, viewerDataMap, contentService); } /** * * @return The real viewer. */ public Viewer getViewer() { return viewer; } /** * * @param anOldInput * @param aNewInput */ public void inputChanged(Object anOldInput, Object aNewInput) { cachedOldInput = anOldInput; cachedNewInput = aNewInput; } /** * * @param aViewer * @param anOldInput * @param aNewInput */ public void inputChanged(Viewer aViewer, Object anOldInput, Object aNewInput) { viewer = (StructuredViewer) aViewer; cachedOldInput = anOldInput; cachedNewInput = aNewInput; } /** * * @param aContentProvider * @return True if all is well. */ public boolean initialize(final IStructuredContentProvider aContentProvider) { final boolean[] result = new boolean[1]; SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { if (aContentProvider != null) { aContentProvider.inputChanged(viewer, cachedOldInput, cachedNewInput); } result[0] = true; } }); return result[0]; } /** * */ public void safeRefresh() { final Viewer localViewer = viewer; if (localViewer == null || localViewer.getControl().isDisposed()) return; Display display = localViewer.getControl().getDisplay(); if (display.isDisposed()) return; display.syncExec(new Runnable() { @Override public void run() { if (localViewer.getControl().isDisposed()) return; SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { localViewer.getControl().setRedraw(false); localViewer.refresh(); } }); localViewer.getControl().setRedraw(true); } }); } }