/** * */ package de.tobject.findbugs.view.properties; import java.net.MalformedURLException; import java.net.URL; import org.eclipse.core.resources.IMarker; import org.eclipse.jface.internal.text.html.HTMLTextPresenter; import org.eclipse.jface.text.DefaultInformationControl.IInformationPresenterExtension; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.LocationEvent; import org.eclipse.swt.browser.LocationListener; import org.eclipse.swt.browser.OpenWindowListener; import org.eclipse.swt.browser.WindowEvent; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWebBrowser; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.internal.views.properties.tabbed.view.TabDescriptor; import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyComposite; import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection; import org.eclipse.ui.views.properties.tabbed.ITabDescriptor; import org.eclipse.ui.views.properties.tabbed.ITabSelectionListener; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; import de.tobject.findbugs.FindbugsPlugin; import de.tobject.findbugs.reporter.MarkerUtil; import de.tobject.findbugs.view.explorer.BugGroup; import de.tobject.findbugs.view.explorer.GroupType; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.BugPattern; import edu.umd.cs.findbugs.DetectorFactory; import edu.umd.cs.findbugs.Plugin; /** * @author Andrei * */ public class BugPatternSection extends AbstractPropertySection { private Browser browser; private StyledText htmlControl; private IInformationPresenterExtension presenter; private Composite rootComposite; private BugPattern pattern; private TextPresentation presentation; private ScrolledComposite scrolledComposite; private ControlAdapter listener; private String oldText; private final PropPageTitleProvider titleProvider; private BugInstance bug; private Point scrollSize; private boolean inResize; private ITabSelectionListener tabSelectionListener; private TabbedPropertySheetPage page; protected String browserId; private volatile boolean allowUrlChange; /** * */ public BugPatternSection() { super(); titleProvider = new PropPageTitleProvider(); } @Override public void createControls(Composite parent, final TabbedPropertySheetPage page1) { super.createControls(parent, page1); page = page1; createRootComposite(parent); initScrolledComposite(parent); createBrowser(rootComposite); } private void createRootComposite(Composite parent) { rootComposite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, true); layout.marginLeft = -5; layout.marginTop = -5; layout.marginBottom = -5; layout.marginRight = -5; rootComposite.setLayout(layout); rootComposite.setSize(SWT.DEFAULT, SWT.DEFAULT); tabSelectionListener = new ITabSelectionListener() { /* * interface defined in Eclipse 3.3 remove this crap as soon as we * stop supporting Eclipse 3.3 */ @SuppressWarnings({ "restriction" }) public void tabSelected(TabDescriptor tabDescriptor) { if (!rootComposite.isDisposed() && rootComposite.isVisible() && !tabDescriptor.isSelected()) { updateBrowserSize(); } } /* * interface defined in Eclipse 3.4 */ public void tabSelected(ITabDescriptor tabDescriptor) { if (!rootComposite.isDisposed() && rootComposite.isVisible() && !tabDescriptor.isSelected()) { updateBrowserSize(); } } }; page.addTabSelectionListener(tabSelectionListener); } private void createBrowser(Composite parent) { Color background = page.getWidgetFactory().getColors().getBackground(); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); data.horizontalIndent = 0; data.verticalIndent = 0; try { browser = new Browser(parent, SWT.NO_BACKGROUND); browser.setLayoutData(data); browser.setBackground(background); browser.addOpenWindowListener(new OpenWindowListener() { public void open(WindowEvent event) { event.required = true; // Cancel opening of new windows } }); browser.addLocationListener(new LocationListener() { public void changed(LocationEvent event) { // ignore } public void changing(LocationEvent event) { // fix for SWT code on Won32 platform: it uses "about:blank" // before // set any non-null url. We ignore this url if (allowUrlChange || "about:blank".equals(event.location)) { return; } // disallow changing of property view content event.doit = false; // for any external url clicked by user we should leave // property view openBrowserInEditor(event); } }); } catch (SWTError e) { presentation = new TextPresentation(); htmlControl = new StyledText(parent, SWT.READ_ONLY); getWidgetFactory().adapt(htmlControl); htmlControl.setLayoutData(data); htmlControl.setBackground(background); try { presenter = new HTMLTextPresenter(false); } catch (Exception e2) { FindbugsPlugin plugin = FindbugsPlugin.getDefault(); plugin.logException(new RuntimeException(e.getMessage(), e), "Could not create org.eclipse.swt.widgets.Composite.Browser"); plugin.logException(new RuntimeException(e2.getMessage(), e2), "Could not create org.eclipse.jface.internal.text.html.HTMLTextPresenter"); } } } private void initScrolledComposite(Composite parent) { scrolledComposite = ((TabbedPropertyComposite) page.getControl()).getScrolledComposite(); // same as above but without warning: // Composite p = parent.getParent(); // while(p != null){ // if(p instanceof ScrolledComposite){ // scrolledComposite = (ScrolledComposite) p; // break; // } // p = p.getParent(); // } if (scrolledComposite != null) { listener = new ControlAdapter() { @Override public void controlResized(ControlEvent e) { if (!rootComposite.isDisposed() && rootComposite.isVisible()) { updateBrowserSize(); } } }; scrolledComposite.addControlListener(listener); } } protected void updateDisplay() { String html = null; if (browser != null && !browser.isDisposed()) { html = getHtml(); // required even if html is the same: our client area might be // changed updateBrowserSize(); // avoid flickering if same input if (!html.equals(oldText)) { allowUrlChange = true; browser.setText(html); allowUrlChange = false; } } else { if (htmlControl != null && !htmlControl.isDisposed() && presenter != null) { Rectangle clientArea = updateBrowserSize(); htmlControl.setSize(clientArea.width - 5, clientArea.height - 5); html = getHtml(); try { html = presenter.updatePresentation(rootComposite.getShell().getDisplay(), html, presentation, clientArea.width, clientArea.height); } catch (StringIndexOutOfBoundsException e) { // I can't understand why it happens, but it happens... } htmlControl.setText(html); } } oldText = html; } /** * Updates the browser/scrolledComposite size to avoid second pair of * scrollbars */ private Rectangle updateBrowserSize() { Point newScrollSize = scrolledComposite.getSize(); Rectangle clientArea = scrolledComposite.getClientArea(); Point rootSize = rootComposite.getSize(); if (!inResize && clientArea.width > 0 && clientArea.height > 0 && (!newScrollSize.equals(scrollSize) || clientArea.width != rootSize.x || clientArea.height != rootSize.y)) { scrollSize = newScrollSize; inResize = true; rootComposite.setSize(clientArea.width, clientArea.height); scrolledComposite.setMinSize(clientArea.width, clientArea.height); scrolledComposite.layout(); inResize = false; } return clientArea; } private String getHtml() { if (pattern == null) { return ""; } boolean hasBug = bug != null; StringBuilder text = new StringBuilder(); if (!hasBug) { text.append("<b>Pattern:</b> "); text.append(pattern.getShortDescription()); text.append("<br>"); } else { text.append("<b>Pattern</b> "); } text.append(titleProvider.getDetails(pattern)); text.append("<br><br>"); text.append(pattern.getDetailText()); if (!hasBug) { return text.toString(); } DetectorFactory factory = bug.getDetectorFactory(); if (factory != null) { Plugin plugin = factory.getPlugin(); text.append("<p><small><i>Reported by: ").append(factory.getFullName()); text.append("<br>Contributed by plugin: ").append(plugin.getPluginId()); text.append("<br>Provider: ").append(plugin.getProvider()); String website = plugin.getWebsite(); if (website != null && website.length() > 0) { text.append(" (<a href=\"").append(website).append("\">"); text.append(website).append("</a>)"); } text.append("</i></small>"); } String html = text.toString(); html = "<b>Bug:</b> " + toSafeHtml(bug.getAbridgedMessage()) + "<br>\n" + html; return html; } private String toSafeHtml(String s) { if (s.indexOf(">") >= 0) { s = s.replace(">", ">"); } if (s.indexOf("<") >= 0) { s = s.replace("<", "<"); } return s; } @Override public void setInput(IWorkbenchPart part, ISelection selection) { super.setInput(part, selection); boolean contentChanged = contentChanged(selection); if (contentChanged) { updateDisplay(); } } /** * Updates pattern and bug from selection * * @return true if the content is changed */ private boolean contentChanged(ISelection selection) { boolean existsBefore = pattern != null; if (!(selection instanceof IStructuredSelection)) { bug = null; pattern = null; return existsBefore; } IStructuredSelection selection2 = (IStructuredSelection) selection; Object object = selection2.getFirstElement(); if (object instanceof BugGroup) { BugGroup group = (BugGroup) object; if (group.getType() == GroupType.Pattern) { bug = null; BugPattern data = (BugPattern) group.getData(); BugPattern old = pattern; pattern = data; return old != data; } } else if (object instanceof IMarker) { IMarker marker = (IMarker) object; if (MarkerUtil.isFindBugsMarker(marker)) { BugInstance bugInstance = MarkerUtil.findBugInstanceForMarker(marker); BugInstance old = bug; bug = bugInstance; pattern = bug != null ? bug.getBugPattern() : null; return old != bugInstance; } } return existsBefore; } @Override public void dispose() { if (rootComposite != null && !rootComposite.isDisposed()) { page.removeTabSelectionListener(tabSelectionListener); scrolledComposite.removeControlListener(listener); rootComposite.dispose(); } super.dispose(); } @Override public boolean shouldUseExtraSpace() { return true; } private void openBrowserInEditor(LocationEvent event) { URL url; try { url = new URL(event.location); } catch (MalformedURLException e) { return; } IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport(); try { IWebBrowser newBrowser = support.createBrowser(browserId); browserId = newBrowser.getId(); newBrowser.openURL(url); return; } catch (PartInitException e) { FindbugsPlugin.getDefault().logException(e, "Can't open external browser"); } } }