| /******************************************************************************* |
| * Copyright (c) 2008 The Eclipse Foundation. |
| * 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: |
| * The Eclipse Foundation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.epp.usagedata.internal.gathering.monitors; |
| |
| import org.eclipse.epp.usagedata.internal.gathering.services.UsageDataService; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IPageListener; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IPerspectiveDescriptor; |
| import org.eclipse.ui.IPerspectiveListener; |
| import org.eclipse.ui.IViewSite; |
| import org.eclipse.ui.IWindowListener; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * Instances of the {@link PartUsageMonitor} class monitor the use of parts in |
| * the workbench. More specifically, it is notified whenever a view or editor is |
| * opened, closed, activated (given focus), etc. and sends some of these events |
| * to the UsageDataService. In the spirit of doing far too much in one place, |
| * instances also monitor opening, closing, activation, and deactivation of |
| * workbench windows. While we're at it, instances also monitor the activation |
| * of perspectives. |
| * <p> |
| * When sent the {@link #startMonitoring(UsageDataService)} message, an instance |
| * adds several listeners to various elements of the workbench. Those listeners |
| * are removed when the instance is sent {@link #stopMonitoring()}. The |
| * listeners either record events, or add/remove listeners to windows and pages |
| * as they are opened/closed. |
| * </p> |
| * |
| * @author Wayne Beaton |
| * |
| */ |
| public class PartUsageMonitor implements UsageMonitor { |
| private static final String EMPTY_STRING = ""; //$NON-NLS-1$ |
| |
| private static final String WORKBENCH_BUNDLE_ID = "org.eclipse.ui.workbench"; //$NON-NLS-1$ |
| private static final String PERSPECTIVES_EXTENSION_POINT = "org.eclipse.ui.perspectives"; //$NON-NLS-1$ |
| |
| private static final String WORKBENCH = "workbench"; //$NON-NLS-1$ |
| private static final String PERSPECTIVE = "perspective"; //$NON-NLS-1$ |
| private static final String DEACTIVATED = "deactivated"; //$NON-NLS-1$ |
| private static final String ACTIVATED = "activated"; //$NON-NLS-1$ |
| private static final String CLOSED = "closed"; //$NON-NLS-1$ |
| private static final String OPENED = "opened"; //$NON-NLS-1$ |
| private static final String PART = "part"; //$NON-NLS-1$ |
| private static final String VIEW = "view"; //$NON-NLS-1$ |
| private static final String EDITOR = "editor"; //$NON-NLS-1$ |
| |
| private UsageDataService usageDataService; |
| |
| private IWindowListener windowListener = new IWindowListener() { |
| public void windowOpened(IWorkbenchWindow window) { |
| recordEvent(OPENED, window); |
| hookListener(window); |
| } |
| |
| public void windowClosed(IWorkbenchWindow window) { |
| recordEvent(CLOSED, window); |
| unhookListeners(window); |
| } |
| |
| public void windowActivated(IWorkbenchWindow window) { |
| recordEvent(ACTIVATED, window); |
| } |
| |
| public void windowDeactivated(IWorkbenchWindow window) { |
| recordEvent(DEACTIVATED, window); |
| } |
| |
| }; |
| |
| private IPageListener pageListener = new IPageListener() { |
| public void pageActivated(IWorkbenchPage page) { |
| } |
| |
| public void pageClosed(IWorkbenchPage page) { |
| unhookListeners(page); |
| } |
| |
| public void pageOpened(IWorkbenchPage page) { |
| hookListeners(page); |
| } |
| |
| }; |
| |
| private IPartListener partListener = new IPartListener() { |
| public void partActivated(IWorkbenchPart part) { |
| recordEvent(ACTIVATED, part); |
| } |
| |
| public void partDeactivated(IWorkbenchPart part) { |
| // Don't care. |
| } |
| |
| public void partBroughtToTop(IWorkbenchPart part) { |
| // Don't care. |
| } |
| |
| public void partClosed(IWorkbenchPart part) { |
| recordEvent(CLOSED, part); |
| } |
| |
| public void partOpened(IWorkbenchPart part) { |
| recordEvent(OPENED, part); |
| } |
| }; |
| |
| private IPerspectiveListener perspectiveListener = new IPerspectiveListener() { |
| public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) { |
| recordEvent(ACTIVATED, perspective); |
| } |
| |
| public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) { |
| } |
| |
| }; |
| |
| private ExtensionIdToBundleMapper perspectiveToBundleIdMapper; |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.epp.usagedata.internal.gathering.UsageMonitor#register(org.eclipse.epp.usagedata.internal.gathering.UsageDataService) |
| */ |
| public void startMonitoring(UsageDataService usageDataService) { |
| this.usageDataService = usageDataService; |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| perspectiveToBundleIdMapper = new ExtensionIdToBundleMapper(PERSPECTIVES_EXTENSION_POINT); |
| hookListeners(workbench); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.epp.usagedata.internal.gathering.UsageMonitor#deregister() |
| */ |
| public void stopMonitoring() { |
| final IWorkbench workbench = PlatformUI.getWorkbench(); |
| unhookListeners(workbench); |
| perspectiveToBundleIdMapper.dispose(); |
| } |
| |
| /** |
| * This method hooks a part listener to all currently open |
| * workbench windows. |
| * |
| * @param workbench |
| */ |
| private void hookListeners(final IWorkbench workbench) { |
| workbench.addWindowListener(windowListener); |
| /* |
| * The syncExec code is no longer required. Previously, |
| * we were only applying the listeners to the active workbench window and |
| * Workbench#getActiveWorkbenchWindow() must be called in the ui thread. |
| */ |
| // workbench.getDisplay().syncExec(new Runnable() { |
| // public void run() { |
| for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) { |
| hookListener(window); |
| } |
| // } |
| // }); |
| } |
| |
| private void unhookListeners(final IWorkbench workbench) { |
| // If the display is disposed, then we're shutting down and the |
| // listeners have already been removed. |
| if (workbench.getDisplay().isDisposed()) |
| return; |
| |
| workbench.removeWindowListener(windowListener); |
| |
| // Walk through the workbench windows and unhook the listeners from each |
| // of them. |
| // workbench.getDisplay().syncExec(new Runnable() { |
| // public void run() { |
| for (IWorkbenchWindow window : workbench.getWorkbenchWindows()) { |
| unhookListeners(window); |
| } |
| // } |
| // }); |
| } |
| |
| private void hookListener(IWorkbenchWindow window) { |
| if (window == null) return; |
| window.addPageListener(pageListener); |
| window.addPerspectiveListener(perspectiveListener); |
| for (IWorkbenchPage page : window.getPages()) { |
| hookListeners(page); |
| } |
| } |
| |
| private void unhookListeners(IWorkbenchWindow window) { |
| if (window == null) return; |
| window.removePageListener(pageListener); |
| window.removePerspectiveListener(perspectiveListener); |
| for(IWorkbenchPage page : window.getPages()) { |
| unhookListeners(page); |
| } |
| } |
| |
| private void hookListeners(IWorkbenchPage page) { |
| IPerspectiveDescriptor perspective = page.getPerspective(); |
| if (perspective != null) { |
| recordEvent(ACTIVATED, perspective); |
| } |
| page.addPartListener(partListener); |
| } |
| |
| private void unhookListeners(IWorkbenchPage page) { |
| page.removePartListener(partListener); |
| } |
| |
| protected void recordEvent(String event, IWorkbenchWindow window) { |
| // TODO Hardcoding bundle id for now. |
| // TODO Does an IWorkbenchWindow have an id? |
| usageDataService.recordEvent(event, WORKBENCH, EMPTY_STRING, WORKBENCH_BUNDLE_ID); |
| } |
| |
| protected void recordEvent(String event, IPerspectiveDescriptor perspective) { |
| String id = perspective.getId(); |
| usageDataService.recordEvent(event, PERSPECTIVE, id, perspectiveToBundleIdMapper.getBundleId(id)); |
| } |
| |
| private void recordEvent(String event, IWorkbenchPart part) { |
| IWorkbenchPartSite site = part.getSite(); |
| usageDataService.recordEvent(event, getKind(site), site.getId(), site.getPluginId()); |
| } |
| |
| /** |
| * This method returns the "kind" of thing that's represented by |
| * <code>site</code>. More specifically, this method answers the |
| * extension point from which the thing represented by <code>site</code> |
| * is defined. Should be an editor or view. Answers <code>null</code> if |
| * the "kind" cannot be determined. |
| * |
| * @param site |
| * @return Name of the extension point from which the editor or view is |
| * created, or null if it cannot be determined. |
| */ |
| private String getKind(IWorkbenchPartSite site) { |
| if (site instanceof IEditorSite) |
| return EDITOR; |
| else if (site instanceof IViewSite) |
| return VIEW; |
| return PART; |
| } |
| } |