/******************************************************************************* * Copyright (c) 2015, 2016 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.editor.support.hover; import java.net.URI; import java.net.URLDecoder; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; import org.springframework.ide.eclipse.editor.support.util.HtmlBuffer; import org.springframework.ide.eclipse.editor.support.util.HtmlUtil; import com.google.common.collect.ImmutableList; @SuppressWarnings("restriction") public abstract class HoverInfo extends BrowserInformationControlInput { /** * Fake host name that is used in 'action link' urls so that we can * recognize them as such. */ private static final String ACTION_HOST = "action"; /** * Registry for 'action link ids' so that we can map clicked action link * to a Runnable to execute it. */ private Map<String, Runnable> actions = null; //only created if used /** * Cached html representation of this info. So the info is computed only once. */ private String html; public HoverInfo() { super(null); } @Override public Object getInputElement() { return this; } @Override public String getInputName() { return ""; } /** * Fetches an html-formatted represetation of this element. Subclasses should not * implement this. Instead they should override the 'renderAsHtml' method. */ public final String getHtml() { if (html==null) { html = renderAsHtml(); } return html; } /** * Subclass must implement this method to format its info as html. Clients should * not call this method directly, instead they should call 'getHtml' which will return * a cached copy of what this method computes. */ protected abstract String renderAsHtml(); /** * IJavaElements associated with the hover target. Used by 'open declaration' * action. */ public List<IJavaElement> getJavaElements() { return ImmutableList.of(); } public static HoverInfo withText(final String plainText) { if (plainText!=null) { return new HoverInfo() { @Override public String renderAsHtml() { return HtmlUtil.text2html(plainText); } }; } return null; } /** * Called by the brower information control when a link is about to be followed. This method * can then decide to handle the link (if it corresponds to an actionId registered with the * object. If the link was handled then the linkHanlder should return true to indicate this. */ public boolean handleActionLink(String link) { String actionId = getActionLinkTarget(link); if (actionId!=null) { Runnable action = getAction(actionId); if (action!=null) { action.run(); } return true; } return false; } private Runnable getAction(String actionId) { if (actions!=null) { return actions.get(actionId); } return null; } /** * Creates an 'action' link and adds it to the html buffer. When the user clicks the given * link then the provided runnable is to be executed. */ public void actionLink(HtmlBuffer html, String displayString, Runnable runnable) { String actionId = registerAction(runnable); html.raw("<a href=\"http://"+ACTION_HOST+"/"); html.url(actionId); html.raw("\">"); html.text(displayString); html.raw("</a>"); } private synchronized String registerAction(Runnable runnable) { if (actions==null) { actions = new HashMap<String, Runnable>(); } String actionId = ""+actions.size(); actions.put(actionId, runnable); return actionId; } /** * Extract the 'action id' from a url location, if it represents an action link. Otherwise * returns null. */ private String getActionLinkTarget(String location) { try { if (location!=null) { URI uri = new URI(location); if (ACTION_HOST.equals(uri.getHost())) { String path = URLDecoder.decode(uri.getPath(), "utf8"); while (path.startsWith("/")) { path = path.substring(1); } return path; } } } catch (Exception e) { //ignore } return null; } }