diff --git a/src/main/java/org/scijava/desktop/links/DefaultLinkService.java b/src/main/java/org/scijava/desktop/links/DefaultLinkService.java index 7cc01b1..55d355c 100644 --- a/src/main/java/org/scijava/desktop/links/DefaultLinkService.java +++ b/src/main/java/org/scijava/desktop/links/DefaultLinkService.java @@ -29,6 +29,7 @@ package org.scijava.desktop.links; import java.awt.Desktop; +import java.awt.GraphicsEnvironment; import java.net.URI; import java.util.Arrays; import java.util.Collections; @@ -46,6 +47,8 @@ import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; import org.scijava.service.Service; +import org.scijava.ui.DialogPrompt; +import org.scijava.ui.UIService; /** * Default implementation of {@link LinkService}. @@ -61,6 +64,12 @@ public class DefaultLinkService extends AbstractHandlerService @Parameter(required = false) private PlatformService platformService; + @Parameter(required = false) + private UIService uiService; + + /** Maximum number of characters of an unsupported URI shown to the user. */ + private static final int MAX_URI_DISPLAY_LENGTH = 120; + @EventHandler private void onEvent(final ContextCreatedEvent evt) { // Register URI handler with the desktop system, if possible. @@ -75,6 +84,26 @@ private void onEvent(final ContextCreatedEvent evt) { installSchemes(); } + @Override + public void handleUnsupported(final URI uri) { + final String appName = System.getProperty("scijava.app.name", "This application"); + final String appDir = System.getProperty("scijava.app.directory"); + String shown = String.valueOf(uri); + if (shown.length() > MAX_URI_DISPLAY_LENGTH) { + shown = shown.substring(0, MAX_URI_DISPLAY_LENGTH) + "..."; + } + final String msg = appName + " cannot handle this link:\n\n" + shown + + "\n\nThe component that supports it may be missing or outdated. " + + "Please make sure it is installed and up to date." + + (appDir == null ? "" : "\n\nIf several installations exist in your system, " + + "the one that received this link may not include the component it " + + "needs. This link has been opened by:\n" + appDir); + if (log != null) log.warn(msg.replace("\n\n", " ").replace("\n", " ")); + if (uiService != null && !GraphicsEnvironment.isHeadless()) { + uiService.showDialog(msg, "Unsupported Link", DialogPrompt.MessageType.ERROR_MESSAGE); + } + } + /** * Installs URI schemes with the operating system. *

diff --git a/src/main/java/org/scijava/desktop/links/LinkService.java b/src/main/java/org/scijava/desktop/links/LinkService.java index 2f9a98a..469e91c 100644 --- a/src/main/java/org/scijava/desktop/links/LinkService.java +++ b/src/main/java/org/scijava/desktop/links/LinkService.java @@ -54,14 +54,30 @@ default void handle(final URI uri) { .findFirst(); if (match.isEmpty()) { // No appropriate link handler plugin was found. - final Logger log = log(); - if (log != null) log.debug("No handler for URI: " + uri); + handleUnsupported(uri); return; // no handler for this URI } // Handle the URI using the matching link handler. match.get().handle(uri); } + /** + * Called by {@link #handle(URI)} when no {@link LinkHandler} supports the + * given URI. This typically happens when a link is opened by an installation + * that lacks (or has an outdated version of) the component that implements + * it, since the operating system routes links by scheme only. + *

+ * The default implementation only logs a debug message. Implementations + * with access to a user interface should override it to notify the user. + *

+ * + * @param uri The URI that no handler supports. + */ + default void handleUnsupported(final URI uri) { + final Logger log = log(); + if (log != null) log.debug("No handler for URI: " + uri); + } + default Set getSchemes() { var schemes = new HashSet(); for (final LinkHandler handler : getInstances()) {