1 /*******************************************************************************
2  * Copyright (c) 2003, 2015 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  *******************************************************************************/
14 package org.eclipse.ui.internal.navigator.extensions;
15 
16 import java.util.Iterator;
17 import java.util.LinkedHashSet;
18 import java.util.Set;
19 
20 import org.eclipse.core.runtime.Assert;
21 import org.eclipse.core.runtime.SafeRunner;
22 import org.eclipse.jface.viewers.ILabelProvider;
23 import org.eclipse.jface.viewers.ILabelProviderListener;
24 import org.eclipse.jface.viewers.ITreeContentProvider;
25 import org.eclipse.ui.IMemento;
26 import org.eclipse.ui.internal.navigator.NavigatorContentService;
27 import org.eclipse.ui.internal.navigator.NavigatorSafeRunnable;
28 import org.eclipse.ui.internal.navigator.Policy;
29 import org.eclipse.ui.navigator.ICommonLabelProvider;
30 import org.eclipse.ui.navigator.IExtensionStateModel;
31 import org.eclipse.ui.navigator.IMementoAware;
32 import org.eclipse.ui.navigator.INavigatorContentDescriptor;
33 import org.eclipse.ui.navigator.INavigatorContentExtension;
34 
35 /**
36  *
37  * @since 3.2
38  */
39 public class NavigatorContentExtension implements IMementoAware,
40 		INavigatorContentExtension {
41 
42 	private static final NavigatorContentExtension[] NO_EXTENSIONS = new NavigatorContentExtension[0];
43 
44 	private NavigatorContentService contentService;
45 
46 	private NavigatorContentDescriptor descriptor;
47 
48 	private SafeDelegateTreeContentProvider contentProvider;
49 
50 	private ICommonLabelProvider labelProvider;
51 
52 	private boolean labelProviderInitializationFailed = false;
53 
54 	private boolean contentProviderInitializationFailed = false;
55 
56 	private boolean isDisposed = false;
57 
58 	private IMemento appliedMemento;
59 
60 	private StructuredViewerManager viewerManager;
61 
62 	/**
63 	 * Create an object to manage the instantiated elements from the extension.
64 	 *
65 	 * @param aDescriptor
66 	 *            The descriptor that knows how to create elements and knows the
67 	 *            id of the extension
68 	 * @param aContentService
69 	 *            The content service that will manage this extension
70 	 * @param aViewerManager
71 	 *            The viewer manager that knows how to initialize the content
72 	 *            provider created by this extension.
73 	 */
NavigatorContentExtension(NavigatorContentDescriptor aDescriptor, NavigatorContentService aContentService, StructuredViewerManager aViewerManager)74 	public NavigatorContentExtension(NavigatorContentDescriptor aDescriptor,
75 			NavigatorContentService aContentService,
76 			StructuredViewerManager aViewerManager) {
77 		super();
78 		Assert.isNotNull(aDescriptor);
79 
80 		descriptor = aDescriptor;
81 		contentService = aContentService;
82 		viewerManager = aViewerManager;
83 	}
84 
85 	@Override
getId()86 	public String getId() {
87 		return descriptor.getId();
88 	}
89 
90 	@Override
getDescriptor()91 	public INavigatorContentDescriptor getDescriptor() {
92 		return descriptor;
93 	}
94 
95 	@Override
getContentProvider()96 	public ITreeContentProvider getContentProvider() {
97 		return internalGetContentProvider().getDelegateContentProvider();
98 	}
99 
100 	/**
101 	 *
102 	 * @return The internal content provider that is wrapped by this extension.
103 	 */
internalGetContentProvider()104 	public SafeDelegateTreeContentProvider internalGetContentProvider() {
105 		if (contentProvider != null || contentProviderInitializationFailed) {
106 			return contentProvider;
107 		}
108 		synchronized (this) {
109 			SafeRunner.run(new NavigatorSafeRunnable() {
110 				@Override
111 				public void run() throws Exception {
112 					if (contentProvider == null) {
113 						ITreeContentProvider treeContentProvider = descriptor
114 								.createContentProvider();
115 						if (treeContentProvider != null) {
116 							contentProvider = new SafeDelegateTreeContentProvider(
117 									treeContentProvider);
118 							contentProvider.init(new CommonContentExtensionSite(getId(),
119 									contentService, appliedMemento));
120 							viewerManager.initialize(contentProvider);
121 						} else {
122 							contentProvider = new SafeDelegateTreeContentProvider(
123 									SkeletonTreeContentProvider.INSTANCE);
124 						}
125 					}
126 				}
127 
128 				@Override
129 				public void handleException(Throwable e) {
130 					super.handleException(e);
131 					contentProviderInitializationFailed = true;
132 				}
133 			});
134 
135 			if (contentProviderInitializationFailed) {
136 				contentProvider = new SafeDelegateTreeContentProvider(
137 						SkeletonTreeContentProvider.INSTANCE);
138 			}
139 		}
140 		return contentProvider;
141 	}
142 
143 	@Override
getLabelProvider()144 	public ICommonLabelProvider getLabelProvider() {
145 		if (labelProvider != null || labelProviderInitializationFailed) {
146 			return labelProvider;
147 		}
148 		synchronized (this) {
149 			SafeRunner.run(new NavigatorSafeRunnable() {
150 				@Override
151 				public void run() throws Exception {
152 					if (labelProvider == null) {
153 						ILabelProvider tempLabelProvider = descriptor.createLabelProvider();
154 
155 						if (tempLabelProvider instanceof ICommonLabelProvider) {
156 							labelProvider = (ICommonLabelProvider) tempLabelProvider;
157 							labelProvider.init(new CommonContentExtensionSite(getId(),
158 									contentService, appliedMemento));
159 						} else {
160 							labelProvider = new SafeDelegateCommonLabelProvider(tempLabelProvider);
161 						}
162 
163 						labelProvider.addListener((ILabelProviderListener) contentService
164 								.createCommonLabelProvider());
165 					}
166 				}
167 
168 				@Override
169 				public void handleException(Throwable e) {
170 					super.handleException(e);
171 					labelProviderInitializationFailed = true;
172 				}
173 			});
174 			if (labelProviderInitializationFailed) {
175 				labelProvider = SkeletonLabelProvider.INSTANCE;
176 			}
177 		}
178 		return labelProvider;
179 	}
180 
181 	/**
182 	 * Dispose of any resources acquired during the lifecycle of the extension.
183 	 *
184 	 */
dispose()185 	public void dispose() {
186 		try {
187 			synchronized (this) {
188 
189 				SafeRunner.run(new NavigatorSafeRunnable() {
190 					@Override
191 					public void run() throws Exception {
192 						if (contentProvider != null) {
193 							contentProvider.dispose();
194 						}
195 
196 					}
197 				});
198 
199 				SafeRunner.run(new NavigatorSafeRunnable() {
200 					@Override
201 					public void run() throws Exception {
202 						if (labelProvider != null) {
203 							labelProvider
204 									.removeListener((ILabelProviderListener) contentService
205 											.createCommonLabelProvider());
206 							labelProvider.dispose();
207 						}
208 					}
209 				});
210 
211 			}
212 		} finally {
213 			isDisposed = true;
214 		}
215 	}
216 
217 	@Override
getAdapter(Class<T> adapter)218 	public <T> T getAdapter(Class<T> adapter) {
219 		return null;
220 	}
221 
222 	/**
223 	 * @return Returns the contentProviderInitializationFailed.
224 	 */
hasContentProviderInitializationFailed()225 	public boolean hasContentProviderInitializationFailed() {
226 		return contentProviderInitializationFailed;
227 	}
228 
229 	/**
230 	 * @return Returns the labelProviderInitializationFailed.
231 	 */
hasLabelProviderInitializationFailed()232 	public boolean hasLabelProviderInitializationFailed() {
233 		return labelProviderInitializationFailed;
234 	}
235 
236 	/**
237 	 *
238 	 * @return True if the loading of the content provider has failed.
239 	 */
hasLoadingFailed()240 	public boolean hasLoadingFailed() {
241 		return contentProviderInitializationFailed;
242 	}
243 
244 	@Override
isLoaded()245 	public boolean isLoaded() {
246 		return contentProvider != null;
247 	}
248 
249 	@Override
restoreState(IMemento aMemento)250 	public void restoreState(IMemento aMemento) {
251 		synchronized (this) {
252 			appliedMemento = aMemento;
253 			applyMemento(contentProvider);
254 			applyMemento(labelProvider);
255 
256 		}
257 	}
258 
259 	@Override
saveState(IMemento aMemento)260 	public void saveState(IMemento aMemento) {
261 		synchronized (this) {
262 			if (contentProvider != null) {
263 				contentProvider.saveState(aMemento);
264 			}
265 			if (labelProvider != null) {
266 				labelProvider.saveState(aMemento);
267 			}
268 		}
269 	}
270 
applyMemento(IMementoAware target)271 	private void applyMemento(IMementoAware target) {
272 		if (target != null) {
273 			target.restoreState(appliedMemento);
274 		}
275 
276 	}
277 
complainDisposedIfNecessary()278 	protected final void complainDisposedIfNecessary() {
279 		if (isDisposed) {
280 			throw new IllegalStateException("INavigatorContentExtension " //$NON-NLS-1$
281 					+ descriptor.getId() + " is disposed!"); //$NON-NLS-1$
282 		}
283 	}
284 
285 	@Override
getStateModel()286 	public IExtensionStateModel getStateModel() {
287 		return contentService.getExtensionStateService()
288 				.getExtensionStateModel(getDescriptor());
289 	}
290 
291 	/**
292 	 * @param anElement
293 	 *            The element for the query.
294 	 * @return Returns the overridingExtensions.
295 	 */
getOverridingExtensionsForTriggerPoint( Object anElement)296 	public NavigatorContentExtension[] getOverridingExtensionsForTriggerPoint(
297 			Object anElement) {
298 		return getOverridingExtensions(anElement, TRIGGER_POINT);
299 	}
300 
301 	/**
302 	 *
303 	 * @param anElement
304 	 *            The element for the query.
305 	 * @return Returns the overridingExtensions.
306 	 */
getOverridingExtensionsForPossibleChild( Object anElement)307 	public NavigatorContentExtension[] getOverridingExtensionsForPossibleChild(
308 			Object anElement) {
309 		return getOverridingExtensions(anElement, !TRIGGER_POINT);
310 	}
311 
312 	/**
313 	 *
314 	 * @return Returns the overridingExtensions.
315 	 */
getOverridingExtensions()316 	public NavigatorContentExtension[] getOverridingExtensions() {
317 		return getOverridingExtensions(null, !TRIGGER_POINT);
318 	}
319 
320 	private static final boolean TRIGGER_POINT = true;
321 
322 	/**
323 	 * @param anElement
324 	 *            The element for the query.
325 	 * @return Returns the overridingExtensions.
326 	 */
getOverridingExtensions(Object anElement, boolean triggerPoint)327 	private NavigatorContentExtension[] getOverridingExtensions(Object anElement,
328 			boolean triggerPoint) {
329 		if (!descriptor.hasOverridingExtensions()) {
330 			return NO_EXTENSIONS;
331 		}
332 
333 		NavigatorContentDescriptor overridingDescriptor;
334 		Set overridingExtensions = new LinkedHashSet();
335 		for (Iterator contentDescriptorsItr = descriptor.getOverriddingExtensions().iterator(); contentDescriptorsItr
336 				.hasNext();) {
337 			overridingDescriptor = (NavigatorContentDescriptor) contentDescriptorsItr.next();
338 
339 			if (contentService.isActive(overridingDescriptor.getId())
340 					&& contentService.isVisible(overridingDescriptor.getId())
341 					&& (anElement == null || (triggerPoint ? overridingDescriptor
342 							.isTriggerPoint(anElement) : overridingDescriptor
343 							.isPossibleChild(anElement)))) {
344 				overridingExtensions.add(contentService.getExtension(overridingDescriptor));
345 			}
346 		}
347 		if (overridingExtensions.isEmpty()) {
348 			return NO_EXTENSIONS;
349 		}
350 		if (Policy.DEBUG_EXTENSION_SETUP) {
351 			System.out
352 					.println(this
353 							+ " overriding: " + //$NON-NLS-1$
354 							(triggerPoint ? "(trigger pt: " : "(poss child: ") + anElement + "): " + overridingExtensions); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
355 		}
356 		return (NavigatorContentExtension[]) overridingExtensions
357 				.toArray(new NavigatorContentExtension[overridingExtensions.size()]);
358 	}
359 
360 	@Override
toString()361 	public String toString() {
362 		return descriptor + " Instance"; //$NON-NLS-1$
363 	}
364 }
365