package rocks.inspectit.ui.rcp.editor.root; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.EditorPart; import rocks.inspectit.shared.all.cmr.model.PlatformIdent; import rocks.inspectit.shared.all.util.ObjectUtils; import rocks.inspectit.shared.cs.storage.IStorageData; import rocks.inspectit.shared.cs.storage.LocalStorageData; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.editor.ISubView; import rocks.inspectit.ui.rcp.editor.SubViewFactory; import rocks.inspectit.ui.rcp.editor.composite.AbstractCompositeSubView; import rocks.inspectit.ui.rcp.editor.inputdefinition.InputDefinition; import rocks.inspectit.ui.rcp.editor.preferences.IPreferencePanel; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId.LiveMode; import rocks.inspectit.ui.rcp.formatter.ImageFormatter; import rocks.inspectit.ui.rcp.provider.IInputDefinitionProvider; import rocks.inspectit.ui.rcp.repository.CmrRepositoryChangeListener; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition.OnlineStatus; import rocks.inspectit.ui.rcp.repository.RepositoryDefinition; import rocks.inspectit.ui.rcp.repository.StorageRepositoryDefinition; import rocks.inspectit.ui.rcp.storage.listener.StorageChangeListener; /** * The abstract root editor is the base class of all editors (or more specific the views). * Currently, the only root editor existing is the {@link FormRootEditor}. An editor is used here, * and not a view, because more than one editor can be opened more easily. Plus the * {@link IEditorInput} interface makes it easy to define the input for the views. * <p> * If the same editor is already opened (thus the editorinput is the same as one that is already * opened), the view is switched to existing one. * * @author Patrice Bouillet * */ public abstract class AbstractRootEditor extends EditorPart implements IRootEditor, IInputDefinitionProvider, CmrRepositoryChangeListener, StorageChangeListener { /** * The inner class for the update timer which just calls the * {@link AbstractRootEditor#refresh()} method in the {@link Display} thread. * * @author Patrice Bouillet * */ private final class UpdateTimerTask extends TimerTask { /** * {@inheritDoc} */ @Override public void run() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { try { doRefresh(); } catch (Exception e) { InspectIT.getDefault().createErrorDialog("The update mechanism on the view failed!", e, -1); } } }); } } /** * The update timer used for the automatic update of the contained views. */ private Timer updateTimer; /** * The {@link IPreferencePanel}. */ private IPreferencePanel preferencePanel; /** * The contained subview. */ private ISubView subView; /** * The active subview. */ private ISubView activeSubView; /** * The selection change listener, initialized lazily; <code>null</code> if not yet created. */ private ISelectionChangedListener selectionChangedListener = null; /** * The post selection changed listener. */ private ISelectionChangedListener postSelectionChangedListener = null; /** * The selection object. Needed for comparison with the new one. */ private ISelection selection; /** * Denotes if the active {@link ISubView} is currently maximized. */ private boolean isMaximizedMode = false; /** * Resource manager. */ private ResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources()); /** * {@inheritDoc} */ @Override public void doRefresh() { subView.doRefresh(); } /** * Returns the input definition for this view. * * @return The input definition. */ @Override public InputDefinition getInputDefinition() { InputDefinition inputDefinition = (InputDefinition) getEditorInput().getAdapter(InputDefinition.class); Assert.isNotNull(inputDefinition); return inputDefinition; } /** * Starts the update timer. */ protected void startUpdateTimer() { if (null == updateTimer) { updateTimer = new Timer(); updateTimer.schedule(new UpdateTimerTask(), 0L, getInputDefinition().getUpdateRate()); } } /** * Stops the update timer. */ protected void stopUpdateTimer() { if (null != updateTimer) { updateTimer.cancel(); updateTimer = null; // NOPMD } } /** * {@inheritDoc} */ @Override public void doSave(IProgressMonitor monitor) { } /** * {@inheritDoc} */ @Override public void doSaveAs() { } /** * {@inheritDoc} */ @Override public void init(IEditorSite editorSite, IEditorInput editorInput) throws PartInitException { // check for valid input if (!(editorInput instanceof RootEditorInput)) { throw new PartInitException("Invalid Input: Must be RootEditorInput"); } // set site and input setSite(editorSite); setInput(editorInput); setTitleImage(ImageFormatter.getOverlayedEditorImage(getInputDefinition().getEditorPropertiesData().getPartImage(), getInputDefinition().getRepositoryDefinition(), resourceManager)); this.subView = SubViewFactory.createSubView(getInputDefinition().getId()); this.subView.setRootEditor(this); this.subView.init(); editorSite.setSelectionProvider(new MultiSubViewSelectionProvider(this)); InspectIT.getDefault().getCmrRepositoryManager().addCmrRepositoryChangeListener(this); InspectIT.getDefault().getInspectITStorageManager().addStorageChangeListener(this); } /** * {@inheritDoc} */ @Override public final void createPartControl(Composite parent) { setPartName(getInputDefinition().getEditorPropertiesData().getPartName()); setTitleToolTip(getInputDefinition().getEditorPropertiesData().getPartTooltip()); // fill the view with content. createView(parent); // start the update timer if it is requested. if (getInputDefinition().isAutomaticUpdate()) { startUpdateTimer(); } else { // do an update one time Timer timer = new Timer(); timer.schedule(new UpdateTimerTask(), 0L); } if (null != preferencePanel) { PreferenceEventCallback callback = new PreferenceEventCallback() { /** * {@inheritDoc} */ @Override public void eventFired(PreferenceEvent preferenceEvent) { if (PreferenceId.LIVEMODE.equals(preferenceEvent.getPreferenceId())) { boolean isLiveMode = false; if (null != updateTimer) { stopUpdateTimer(); isLiveMode = true; } if (preferenceEvent.getPreferenceMap().containsKey(LiveMode.REFRESH_RATE)) { long refresh = (Long) preferenceEvent.getPreferenceMap().get(LiveMode.REFRESH_RATE); getInputDefinition().setUpdateRate(refresh); } if (preferenceEvent.getPreferenceMap().containsKey(LiveMode.BUTTON_LIVE_ID)) { isLiveMode = (Boolean) preferenceEvent.getPreferenceMap().get(LiveMode.BUTTON_LIVE_ID); } if (isLiveMode) { startUpdateTimer(); } else { stopUpdateTimer(); } } else if (PreferenceId.UPDATE.equals(preferenceEvent.getPreferenceId())) { doRefresh(); } getSubView().preferenceEventFired(preferenceEvent); } }; preferencePanel.registerCallback(callback); } } /** * Create the view in the subclasses. * * @param parent * The parent composite. */ protected abstract void createView(Composite parent); /** * Sets the preference panel. Throws {@link NullPointerException} if * <code>preferencePanel</code> is set to <code>null</code>. * * @param preferencePanel * The preference panel. */ protected void setPreferencePanel(IPreferencePanel preferencePanel) { Assert.isNotNull(preferencePanel); this.preferencePanel = preferencePanel; } /** * {@inheritDoc} */ @Override public IPreferencePanel getPreferencePanel() { return preferencePanel; } /** * {@inheritDoc} */ @Override public boolean isDirty() { // can never be true return false; } /** * {@inheritDoc} */ @Override public boolean isSaveAsAllowed() { // can never be true, nothing to save. return false; } /** * {@inheritDoc} */ @Override public void setFocus() { if ((null != getActiveSubView()) && (null != getActiveSubView().getControl())) { getActiveSubView().getControl().setFocus(); } else if ((null != subView) && (null != subView.getControl())) { subView.getControl().setFocus(); } } /** * {@inheritDoc} */ @Override public ISubView getSubView() { return subView; } /** * {@inheritDoc} */ @Override public void setActiveSubView(ISubView subView) { Assert.isNotNull(subView); if (!ObjectUtils.equals(activeSubView, subView)) { this.activeSubView = subView; ISelectionProvider selectionProvider = activeSubView.getSelectionProvider(); if (selectionProvider != null) { ISelectionProvider outerProvider = getSite().getSelectionProvider(); if (outerProvider instanceof MultiSubViewSelectionProvider) { ISelection newSelection = selectionProvider.getSelection(); MultiSubViewSelectionProvider provider = (MultiSubViewSelectionProvider) outerProvider; if (ObjectUtils.equals(selection, newSelection)) { // The selection object didn't change, but the selection area did, thus we // have to simulate a little bit. Problem is due to TreeSelection#equals // passing equal comparison to the parent class (which means that a // treeselection and a structuredselection are equal if they refer to the // same selection). See bug at // https://bugs.eclipse.org/bugs/show_bug.cgi?id=135837 SelectionChangedEvent event = new SelectionChangedEvent(selectionProvider, StructuredSelection.EMPTY); provider.fireSelectionChanged(event); provider.firePostSelectionChanged(event); } // now the real event selection = newSelection; SelectionChangedEvent event = new SelectionChangedEvent(selectionProvider, selection); provider.fireSelectionChanged(event); provider.firePostSelectionChanged(event); } } } } /** * {@inheritDoc} */ @Override public ISubView getActiveSubView() { return activeSubView; } /** * {@inheritDoc} */ @Override public void setDataInput(List<? extends Object> data) { subView.setDataInput(data); } /** * Returns the selection changed listener which listens to the nested editor's selection * changes, and calls <code>handleSelectionChanged</code> . * * @return the selection changed listener */ public ISelectionChangedListener getSelectionChangedListener() { if (selectionChangedListener == null) { selectionChangedListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { AbstractRootEditor.this.handleSelectionChanged(event); } }; } return selectionChangedListener; } /** * Returns the post selection change listener which listens to the nested editor's selection * changes. * * @return the post selection change listener. */ public ISelectionChangedListener getPostSelectionChangedListener() { if (postSelectionChangedListener == null) { postSelectionChangedListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { AbstractRootEditor.this.handlePostSelectionChanged(event); } }; } return postSelectionChangedListener; } /** * Handles a selection changed event from the nested sub view. The default implementation gets * the selection provider from the site, and calls <code>fireSelectionChanged</code> on it (only * if it is an instance of <code>MultiSubViewSelectionProvider</code>), passing a new event * object. * * @param event * The event. */ protected void handleSelectionChanged(SelectionChangedEvent event) { ISelectionProvider provider = getSite().getSelectionProvider(); if (provider instanceof MultiSubViewSelectionProvider) { SelectionChangedEvent newEvent = new SelectionChangedEvent(provider, event.getSelection()); MultiSubViewSelectionProvider prov = (MultiSubViewSelectionProvider) provider; prov.fireSelectionChanged(newEvent); } } /** * Handles a post selection changed even from the active sub view. * * @param event * The event */ protected void handlePostSelectionChanged(SelectionChangedEvent event) { ISelectionProvider provider = getSite().getSelectionProvider(); if (provider instanceof MultiSubViewSelectionProvider) { SelectionChangedEvent newEvent = new SelectionChangedEvent(provider, event.getSelection()); MultiSubViewSelectionProvider prov = (MultiSubViewSelectionProvider) provider; prov.firePostSelectionChanged(newEvent); } } /** * Sets the current selection object. * * @param selection * the selection object to set. */ public void setSelection(ISelection selection) { this.selection = selection; } /** * Returns if the currently active sub-view can be maximized. Note that only if a * {@link AbstractCompositeSubView} is a main sub-view of the editor this action is possible. In * addition to that, number of sub-view has to be larger of 1 and currently no view has to be * maximized. * <p> * If {@link #activeSubView} is null, this method will still return true if all the conditions * above are fulfilled. Note that in this case, maximize action will maximize the first view. * * @return True if the maximize active sub-view action can be executed. */ public boolean canMaximizeActiveSubView() { if (isMaximizedMode) { return false; } else { if (subView instanceof AbstractCompositeSubView) { AbstractCompositeSubView compositeSubView = (AbstractCompositeSubView) subView; List<ISubView> allViews = compositeSubView.getSubViews(); if ((null != allViews) && (allViews.size() > 1)) { return true; } } return false; } } /** * Returns if the active sub view is currently maximized and thus can be minimized. * * @return Returns if the active sub view is currently maximized and thus can be minimized. */ public boolean canMinimizeActiveSubView() { return isMaximizedMode; } /** * Maximizes the active sub-view if that is possible. */ public void maximizeActiveSubView() { if (canMaximizeActiveSubView()) { maximizeSubView((AbstractCompositeSubView) subView, activeSubView); isMaximizedMode = true; ISelectionProvider selectionProvider = getSite().getSelectionProvider(); selectionProvider.setSelection(StructuredSelection.EMPTY); } } /** * Recursively maximizes all needed sub-views until the active one is maximized. * * @param compositeSubView * {@link AbstractCompositeSubView} to start from. * @param view * View to maximize. * @return True if the composite sub view did the maximization. */ private boolean maximizeSubView(AbstractCompositeSubView compositeSubView, ISubView view) { if (null == view) { compositeSubView.maximizeSubView(null); return true; } if (compositeSubView.getSubViews().contains(view)) { compositeSubView.maximizeSubView(view); return true; } else { for (ISubView viewInComposite : compositeSubView.getSubViews()) { if (viewInComposite instanceof AbstractCompositeSubView) { boolean maximized = maximizeSubView((AbstractCompositeSubView) viewInComposite, view); if (maximized) { compositeSubView.maximizeSubView(viewInComposite); return true; } } } } return false; } /** * Minimizes the active sub-view if that is possible. */ public void minimizeActiveSubView() { if (canMinimizeActiveSubView()) { restoreMaximization((AbstractCompositeSubView) subView); isMaximizedMode = false; ISelectionProvider selectionProvider = getSite().getSelectionProvider(); selectionProvider.setSelection(StructuredSelection.EMPTY); } } /** * Recursively restores the maximized mode. * * @param compositeSubView * {@link AbstractCompositeSubView} to start from. */ private void restoreMaximization(AbstractCompositeSubView compositeSubView) { compositeSubView.restoreMaximization(); for (ISubView viewInComposite : compositeSubView.getSubViews()) { if (viewInComposite instanceof AbstractCompositeSubView) { restoreMaximization((AbstractCompositeSubView) viewInComposite); } } } /** * Returns if the editor had the active sub-view maximized. * * @return Returns if the editor had the active sub-view maximized. */ public boolean isActiveViewMaximized() { return isMaximizedMode; } /** * Updates the name of the editor. * * @param name * New name. */ public void updateEditorName(String name) { if (StringUtils.isNotEmpty(name)) { setPartName(name); } } /** * {@inheritDoc} */ @Override public void repositoryOnlineStatusUpdated(CmrRepositoryDefinition repositoryDefinition, OnlineStatus oldStatus, OnlineStatus newStatus) { } /** * {@inheritDoc} */ @Override public void repositoryAdded(CmrRepositoryDefinition cmrRepositoryDefinition) { } /** * {@inheritDoc} */ @Override public void repositoryRemoved(CmrRepositoryDefinition cmrRepositoryDefinition) { if (ObjectUtils.equals(cmrRepositoryDefinition, getInputDefinition().getRepositoryDefinition())) { close(); } else if (getInputDefinition().getRepositoryDefinition() instanceof StorageRepositoryDefinition) { // close also if storage is displayed from the repository that is removed StorageRepositoryDefinition storageRepositoryDefinition = (StorageRepositoryDefinition) getInputDefinition().getRepositoryDefinition(); if (ObjectUtils.equals(cmrRepositoryDefinition, storageRepositoryDefinition.getCmrRepositoryDefinition()) && !storageRepositoryDefinition.getLocalStorageData().isFullyDownloaded()) { close(); } } } /** * {@inheritDoc} */ @Override public void repositoryDataUpdated(CmrRepositoryDefinition cmrRepositoryDefinition) { } /** * {@inheritDoc} */ @Override public void repositoryAgentDeleted(CmrRepositoryDefinition cmrRepositoryDefinition, PlatformIdent agent) { if (ObjectUtils.equals(cmrRepositoryDefinition, getInputDefinition().getRepositoryDefinition())) { if (agent.getId() == getInputDefinition().getIdDefinition().getPlatformId()) { close(); } } } /** * {@inheritDoc} */ @Override public void storageDataUpdated(IStorageData storageData) { } /** * {@inheritDoc} */ @Override public void storageRemotelyDeleted(IStorageData storageData) { RepositoryDefinition repositoryDefinition = getInputDefinition().getRepositoryDefinition(); if (repositoryDefinition instanceof StorageRepositoryDefinition) { LocalStorageData localStorageData = ((StorageRepositoryDefinition) repositoryDefinition).getLocalStorageData(); if (!localStorageData.isFullyDownloaded() && ObjectUtils.equals(localStorageData.getId(), storageData.getId())) { close(); } } } /** * {@inheritDoc} */ @Override public void storageLocallyDeleted(IStorageData storageData) { RepositoryDefinition repositoryDefinition = getInputDefinition().getRepositoryDefinition(); if (repositoryDefinition instanceof StorageRepositoryDefinition) { LocalStorageData localStorageData = ((StorageRepositoryDefinition) repositoryDefinition).getLocalStorageData(); if (ObjectUtils.equals(localStorageData.getId(), storageData.getId())) { if (!InspectIT.getDefault().getInspectITStorageManager().getMountedAvailableStorages().contains(storageData)) { // close only if the remote one is also not available close(); } } } } /** * Closes the editor. */ protected void close() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { getEditorSite().getPage().closeEditor(AbstractRootEditor.this, false); } }); } /** * {@inheritDoc} */ @Override public void dispose() { InspectIT.getDefault().getCmrRepositoryManager().removeCmrRepositoryChangeListener(this); InspectIT.getDefault().getInspectITStorageManager().removeStorageChangeListener(this); // stop the timer if it is active stopUpdateTimer(); // dispose the local resource manager resourceManager.dispose(); super.dispose(); // dispose the preference panel if (null != preferencePanel) { preferencePanel.dispose(); } if (null != subView) { subView.dispose(); } } }