1 /*******************************************************************************
2  * Copyright (c) 2003, 2015, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Fair Issac Corp - bug 287103 - NCSLabelProvider does not properly handle overrides
14  *     Stefan Winkler <stefan@winklerweb.net> - bug 178019 - CNF Tooltip support
15  *******************************************************************************/
16 package org.eclipse.ui.internal.navigator;
17 
18 
19 import java.util.Collection;
20 import java.util.Iterator;
21 
22 import org.eclipse.core.commands.common.EventManager;
23 import org.eclipse.core.runtime.SafeRunner;
24 import org.eclipse.jface.util.SafeRunnable;
25 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
26 import org.eclipse.jface.viewers.IColorProvider;
27 import org.eclipse.jface.viewers.IFontProvider;
28 import org.eclipse.jface.viewers.ILabelProvider;
29 import org.eclipse.jface.viewers.ILabelProviderListener;
30 import org.eclipse.jface.viewers.ITableLabelProvider;
31 import org.eclipse.jface.viewers.IToolTipProvider;
32 import org.eclipse.jface.viewers.ITreePathLabelProvider;
33 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
34 import org.eclipse.jface.viewers.StyledString;
35 import org.eclipse.jface.viewers.TreePath;
36 import org.eclipse.jface.viewers.ViewerLabel;
37 import org.eclipse.osgi.util.NLS;
38 import org.eclipse.swt.graphics.Color;
39 import org.eclipse.swt.graphics.Font;
40 import org.eclipse.swt.graphics.Image;
41 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
42 import org.eclipse.ui.navigator.CommonViewer;
43 import org.eclipse.ui.navigator.ICommonLabelProvider;
44 import org.eclipse.ui.navigator.INavigatorContentService;
45 
46 /**
47  * <p>
48  * Provides relevant labels based on the associated
49  * {@link INavigatorContentService}for the contents of a
50  * TreeViewer.
51  * </p>
52  *
53  * <p>
54  * Except for the dependency on
55  * {@link INavigatorContentService}, this class has no
56  * dependencies on the rest of the Common Navigator framework. Tree viewers that would like to use
57  * the extensions defined by the Common Navigator, without using the actual view part or other
58  * pieces of functionality (filters, sorting, etc) may choose to use this class, in effect using an
59  * extensible label provider.
60  * </p>
61  *
62  * @since 3.2
63  *
64  * @see org.eclipse.ui.internal.navigator.NavigatorContentService
65  * @see org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider
66  */
67 public class NavigatorContentServiceLabelProvider extends EventManager
68 		implements ILabelProvider, IColorProvider, IFontProvider, ITreePathLabelProvider, ITableLabelProvider,
69 		ILabelProviderListener, IStyledLabelProvider, IToolTipProvider {
70 
71 	private final NavigatorContentService contentService;
72 	private final boolean isContentServiceSelfManaged;
73 	private final ReusableViewerLabel reusableLabel = new ReusableViewerLabel();
74 
75 
76 	/**
77 	 * <p>
78 	 * Uses the supplied content service to acquire the available extensions.
79 	 * </p>
80 	 *
81 	 * @param aContentService
82 	 *            The associated NavigatorContentService that should be used to acquire information.
83 	 */
NavigatorContentServiceLabelProvider(NavigatorContentService aContentService)84 	public NavigatorContentServiceLabelProvider(NavigatorContentService aContentService) {
85 		contentService = aContentService;
86 		isContentServiceSelfManaged = false;
87 	}
88 
89 	/**
90 	 * <p>
91 	 * Return the appropriate image for anElement. The image will be used as the icon for anElement
92 	 * when displayed in the tree viewer. This method uses information from its contentService to
93 	 * know what extensions to use to supply the correct label.
94 	 * </p>
95 	 * {@inheritDoc}
96 	 *
97 	 * @param anElement
98 	 *            An element from the Tree Viewer
99 	 * @return The Image that will be used as the icon when anElement is displayed in the viewer.
100 	 * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
101 	 */
102 	@Override
getImage(Object anElement)103 	public Image getImage(Object anElement) {
104 		return getColumnImage(anElement, -1);
105 	}
106 
107 	@Override
getColumnImage(Object element, int columnIndex)108 	public Image getColumnImage(Object element, int columnIndex) {
109 		Collection contentExtensions = contentService.findPossibleLabelExtensions(element);
110 		Image image = null;
111 		for (Iterator itr = contentExtensions.iterator(); itr.hasNext() && image == null; ) {
112 			image = findImage((NavigatorContentExtension) itr.next(), element, columnIndex);
113 		}
114 		return image;
115 	}
116 
117 	/**
118 	 * <p>
119 	 * Return a String representation of anElement to be used as the display name in the tree
120 	 * viewer.
121 	 * </p>
122 	 *
123 	 * @param anElement
124 	 *            An element from the Tree Viewer
125 	 * @return The String label to display for the object when represented in the viewer.
126 	 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
127 	 */
128 	@Override
getText(Object anElement)129 	public String getText(Object anElement) {
130 		return getColumnText(anElement, -1);
131 	}
132 
133 	@Override
getColumnText(Object anElement, int aColumn)134 	public String getColumnText(Object anElement, int aColumn) {
135 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
136 		if (labelProviders.length == 0)
137 			return NLS.bind(CommonNavigatorMessages.NavigatorContentServiceLabelProvider_Error_no_label_provider_for_0_, makeSmallString(anElement));
138 		String text = null;
139 		for (ILabelProvider labelProvider : labelProviders) {
140 			if (labelProvider instanceof ITableLabelProvider && aColumn != -1)
141 				text = ((ITableLabelProvider)labelProvider).getColumnText(anElement, aColumn);
142 			else
143 				text = labelProvider.getText(anElement);
144 			if (text != null && text.length() > 0)
145 				return text;
146 		}
147 		return text;
148 	}
149 
150 	@Override
getStyledText(Object anElement)151 	public StyledString getStyledText(Object anElement) {
152 		Collection extensions = contentService.findPossibleLabelExtensions(anElement);
153 		if (extensions.isEmpty())
154 			return new StyledString(NLS.bind(CommonNavigatorMessages.NavigatorContentServiceLabelProvider_Error_no_label_provider_for_0_, makeSmallString(anElement)));
155 
156 		StyledString text = null;
157 		for (Iterator itr = extensions.iterator(); itr.hasNext() && text == null; ) {
158 			text = findStyledText((NavigatorContentExtension) itr.next(), anElement);
159 		}
160 		return text != null ? text : new StyledString();
161 	}
162 
163 	/**
164 	 * Search for a styled text label and take overrides into account.
165 	 * Uses only simple ITreeContentProvider.getParent() style semantics.
166 	 *
167 	 * @return the styled text or <code>null</code> if no extension has been found that provides a label
168 	 */
findStyledText(NavigatorContentExtension foundExtension, Object anElement)169 	private StyledString findStyledText(NavigatorContentExtension foundExtension, Object anElement) {
170 		ICommonLabelProvider labelProvider= foundExtension.getLabelProvider();
171 		if (labelProvider instanceof IStyledLabelProvider) {
172 			StyledString styledText= ((IStyledLabelProvider) labelProvider).getStyledText(anElement);
173 			// paranoia check for null, although null is not a valid return value for IStyledLabelProvider.getStyledText
174 			if (styledText != null && styledText.length() > 0) {
175 				return styledText;
176 			}
177 		} else {
178 			String text= labelProvider.getText(anElement);
179 			if (text != null && text.length() > 0) {
180 				return new StyledString(text);
181 			}
182 		}
183 		return null;
184 	}
185 
makeSmallString(Object obj)186 	private String makeSmallString(Object obj) {
187 		if (obj == null)
188 			return "null"; //$NON-NLS-1$
189 		String str = obj.toString();
190 		int len = str.length();
191 		return str.substring(0, len < 50 ? len : 49);
192 	}
193 
194 	/**
195 	 * Search for image and take overrides into account.
196 	 * Uses only simple ITreeContentProvider.getParent() style semantics.
197 	 */
findImage(NavigatorContentExtension foundExtension, Object anElement, int aColumn)198 	private Image findImage(NavigatorContentExtension foundExtension, Object anElement, int aColumn) {
199 		Image image = null;
200 		ICommonLabelProvider provider = foundExtension.getLabelProvider();
201 		if (provider instanceof ITableLabelProvider && aColumn >= 0)
202 			image = ((ITableLabelProvider)provider).getColumnImage(anElement, aColumn);
203 		else
204 			image = provider.getImage(anElement);
205 
206 		return image;
207 	}
208 
209 	@Override
getFont(Object anElement)210 	public Font getFont(Object anElement) {
211 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
212 		for (ILabelProvider provider : labelProviders) {
213 			if (provider instanceof IFontProvider) {
214 				IFontProvider fontProvider = (IFontProvider) provider;
215 				Font font = fontProvider.getFont(anElement);
216 				if (font != null) {
217 					return font;
218 				}
219 			}
220 		}
221 		return null;
222 	}
223 
224 	@Override
getForeground(Object anElement)225 	public Color getForeground(Object anElement) {
226 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
227 		for (ILabelProvider provider : labelProviders) {
228 			if (provider instanceof IColorProvider) {
229 				IColorProvider colorProvider = (IColorProvider) provider;
230 				Color color = colorProvider.getForeground(anElement);
231 				if (color != null) {
232 					return color;
233 				}
234 			}
235 		}
236 		return null;
237 	}
238 
239 	@Override
getBackground(Object anElement)240 	public Color getBackground(Object anElement) {
241 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
242 		for (ILabelProvider provider : labelProviders) {
243 			if (provider instanceof IColorProvider) {
244 				IColorProvider colorProvider = (IColorProvider) provider;
245 				Color color = colorProvider.getBackground(anElement);
246 				if (color != null) {
247 					return color;
248 				}
249 			}
250 		}
251 		return null;
252 	}
253 
254 	@Override
getToolTipText(Object anElement)255 	public String getToolTipText(Object anElement) {
256 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
257 		for (ILabelProvider provider : labelProviders) {
258 			if (provider instanceof IToolTipProvider) {
259 				IToolTipProvider tooltipProvider = (IToolTipProvider) provider;
260 				String tooltip = tooltipProvider.getToolTipText(anElement);
261 				if (tooltip != null && !tooltip.isEmpty()) {
262 					return tooltip;
263 				}
264 			}
265 		}
266 		return null;
267 	}
268 
269 	/**
270 	 * <p>
271 	 * Indicates whether anElelement has aProperty that affects the display of the label.
272 	 * </p>
273 	 * {@inheritDoc}
274 	 *
275 	 * @param anElement
276 	 *            An element from the Tree Viewer
277 	 * @param aProperty
278 	 *            A property of the given element that could be a label provider
279 	 * @return True if any of the extensions enabled on anElement consider aProperty a
280 	 *         label-changing property.
281 	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
282 	 *      java.lang.String)
283 	 */
284 	@Override
isLabelProperty(Object anElement, String aProperty)285 	public boolean isLabelProperty(Object anElement, String aProperty) {
286 		boolean result = false;
287 		ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement);
288 		for (int i = 0; i < labelProviders.length && !result; i++) {
289 			result = labelProviders[i].isLabelProperty(anElement, aProperty);
290 		}
291 		return result;
292 	}
293 
294 
295 	/**
296 	 * <p>
297 	 * Label provider listeners are currently supported.
298 	 * </p>
299 	 *
300 	 * {@inheritDoc}
301 	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
302 	 */
303 	@Override
addListener(ILabelProviderListener aListener)304 	public void addListener(ILabelProviderListener aListener) {
305 		addListenerObject(aListener);
306 	}
307 
308 	/**
309 	 * <p>
310 	 * Label provider listeners are currently supported.
311 	 * </p>
312 	 *
313 	 * {@inheritDoc}
314 	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
315 	 */
316 	@Override
removeListener(ILabelProviderListener aListener)317 	public void removeListener(ILabelProviderListener aListener) {
318 		removeListenerObject(aListener);
319 	}
320 
321 	/**
322 	 * <p>
323 	 * Dispose of the content service, if it was created and not supplied.
324 	 * </p>
325 	 * <p>
326 	 * <b>If a client uses this class outside of the framework of {@link CommonViewer}, this method
327 	 * must be called when finished. </b>
328 	 * </p>
329 	 *
330 	 * {@inheritDoc}
331 	 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
332 	 */
333 	@Override
dispose()334 	public void dispose() {
335 		if (isContentServiceSelfManaged) {
336 			contentService.dispose();
337 		}
338 
339 	}
340 
341 	/**
342 	 * Fires a label provider changed event to all registered listeners
343 	 * Only listeners registered at the time this method is called are notified.
344 	 *
345 	 * @param event a label provider changed event
346 	 *
347 	 * @see ILabelProviderListener#labelProviderChanged
348 	 */
fireLabelProviderChanged( final LabelProviderChangedEvent event)349 	protected void fireLabelProviderChanged(
350 			final LabelProviderChangedEvent event) {
351 		for (Object listener : getListeners()) {
352 			final ILabelProviderListener labelProviderListener = (ILabelProviderListener) listener;
353 			SafeRunner.run(new SafeRunnable() {
354 				@Override
355 				public void run() {
356 					labelProviderListener.labelProviderChanged(event);
357 				}
358 			});
359 
360 		}
361 	}
362 
363 	@Override
updateLabel(ViewerLabel label, TreePath elementPath)364 	public void updateLabel(ViewerLabel label, TreePath elementPath) {
365 
366 		Collection contentExtensions = contentService.findPossibleLabelExtensions(elementPath.getLastSegment());
367 		reusableLabel.reset(label);
368 		for (Iterator itr = contentExtensions.iterator(); itr.hasNext() && !(reusableLabel.isValid() && reusableLabel.hasChanged()); ) {
369 			findUpdateLabel((NavigatorContentExtension)itr.next(), reusableLabel, elementPath);
370 		}
371 		reusableLabel.fill(label);
372 	}
373 
374 
375 	/**
376 	 * Search for text label and take overrides into account.
377 	 * Uses only simple ITreeContentProvider.getParent() style semantics.
378 	 */
findUpdateLabel(NavigatorContentExtension foundExtension, ReusableViewerLabel label, TreePath elementPath)379 	private void findUpdateLabel(NavigatorContentExtension foundExtension, ReusableViewerLabel label, TreePath elementPath) {
380 
381 		ILabelProvider labelProvider = foundExtension.getLabelProvider();
382 		if (labelProvider instanceof ITreePathLabelProvider) {
383 			ITreePathLabelProvider tplp = (ITreePathLabelProvider) labelProvider;
384 			tplp.updateLabel(label, elementPath);
385 		} else {
386 			label.setImage(labelProvider.getImage(elementPath.getLastSegment()));
387 			label.setText(labelProvider.getText(elementPath.getLastSegment()));
388 		}
389 	}
390 
391 	@Override
labelProviderChanged(LabelProviderChangedEvent event)392 	public void labelProviderChanged(LabelProviderChangedEvent event) {
393 		fireLabelProviderChanged(event);
394 	}
395 
396 
397 
398 }
399