diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/registry/PerspectiveRegistry.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/registry/PerspectiveRegistry.java index 46077dc7e92..edb82787e8c 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/registry/PerspectiveRegistry.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/registry/PerspectiveRegistry.java @@ -33,6 +33,7 @@ import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.e4.core.contexts.ContextInjectionFactory; @@ -58,6 +59,9 @@ */ public class PerspectiveRegistry implements IPerspectiveRegistry, IExtensionChangeHandler { + private static final String PLATFORM_PLUGIN_PREFIX = "platform:/plugin/"; //$NON-NLS-1$ + private static final String PLATFORM_FRAGMENT_PREFIX = "platform:/fragment/"; //$NON-NLS-1$ + @Inject private IExtensionRegistry extensionRegistry; @@ -137,19 +141,49 @@ public void createDescriptor(MPerspective perspective) { String id = perspective.getElementId(); PerspectiveDescriptor newDescriptor = new PerspectiveDescriptor(id, label, originalDescriptor); - if (perspective.getIconURI() != null) { + String iconURI = perspective.getIconURI(); + if (iconURI != null && isIconAvailable(iconURI)) { try { - ImageDescriptor img = ImageDescriptor.createFromURL(new URI(perspective.getIconURI()).toURL()); + ImageDescriptor img = ImageDescriptor.createFromURL(new URI(iconURI).toURL()); newDescriptor.setImageDescriptor(img); } catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) { logger.warn(e, MessageFormat.format("Error on applying configured perspective icon: {0}", //$NON-NLS-1$ - perspective.getIconURI())); + iconURI)); } } descriptors.put(id, newDescriptor); } + /** + * Tells whether the bundle a platform icon URI points to is installed. Loading + * an icon of a missing bundle logs an error per attempt in the platform URL + * layer. + */ + private boolean isIconAvailable(String iconURI) { + String reference = null; + for (String prefix : new String[] { PLATFORM_PLUGIN_PREFIX, PLATFORM_FRAGMENT_PREFIX }) { + if (iconURI.startsWith(prefix)) { + reference = iconURI.substring(prefix.length()).split("/", 2)[0]; //$NON-NLS-1$ + break; + } + } + if (reference == null) { + return true; + } + if (Platform.getBundle(reference) != null) { + return true; + } + // the reference may carry a version, as in "com.example_1.0.0" + int underscore = reference.indexOf('_'); + if (underscore > 0 && Platform.getBundle(reference.substring(0, underscore)) != null) { + return true; + } + logger.warn(MessageFormat.format("Skipping the perspective icon {0} of the not installed bundle {1}", //$NON-NLS-1$ + iconURI, reference)); + return false; + } + /** * Construct a new registry. */ diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/api/IPerspectiveRegistryTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/api/IPerspectiveRegistryTest.java index 6e3c65ed112..b82a72219f2 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/api/IPerspectiveRegistryTest.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/api/IPerspectiveRegistryTest.java @@ -27,8 +27,10 @@ import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.ui.IPerspectiveDescriptor; import org.eclipse.ui.IPerspectiveRegistry; +import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.WorkbenchImages; import org.eclipse.ui.internal.WorkbenchWindow; import org.eclipse.ui.tests.harness.util.ArrayUtil; import org.junit.Before; @@ -139,6 +141,43 @@ public void testModelPerspective() { } } + /** + * A perspective whose contributing bundle is gone must fall back to the default + * icon instead of loading the one its model element names. + */ + @Test + public void testModelPerspectiveWithUninstalledIconBundle() { + WorkbenchWindow window = (WorkbenchWindow) PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + EModelService modelService = window.getService(EModelService.class); + + List stacks = modelService.findElements(window.getModel(), null, MPerspectiveStack.class); + assertFalse("expected a perspective stack in the active window", stacks.isEmpty()); + MPerspectiveStack stack = stacks.get(0); + + String id = "org.eclipse.ui.tests.perspectiveWithUninstalledIconBundle"; + MPerspective perspective = modelService.createModelElement(MPerspective.class); + perspective.setElementId(id); + perspective.setLabel("Perspective With Uninstalled Icon Bundle"); + perspective.setIconURI("platform:/plugin/org.eclipse.ui.tests.uninstalled/icons/missing.svg"); + perspective.setToBeRendered(false); + stack.getChildren().add(perspective); + + try { + fReg.getPerspectives(); // registers perspectives added to the model + IPerspectiveDescriptor descriptor = fReg.findPerspectiveWithId(id); + assertNotNull("model-contributed perspective must be resolvable by id", descriptor); + assertEquals("the default perspective icon must be used for an uninstalled bundle", + WorkbenchImages.getImageDescriptor(ISharedImages.IMG_ETOOL_DEF_PERSPECTIVE), + descriptor.getImageDescriptor()); + } finally { + stack.getChildren().remove(perspective); + IPerspectiveDescriptor descriptor = fReg.findPerspectiveWithId(id); + if (descriptor != null) { + fReg.deletePerspective(descriptor); + } + } + } + @Test @Ignore public void XXXtestDeleteClonedPerspective() {