package rocks.inspectit.ui.rcp.ci.view;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections.CollectionUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import rocks.inspectit.shared.all.cmr.model.PlatformIdent;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.formatter.ImageFormatter;
import rocks.inspectit.ui.rcp.formatter.TextFormatter;
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.CmrRepositoryManager;
import rocks.inspectit.ui.rcp.util.SafeExecutor;
import rocks.inspectit.ui.rcp.util.SelectionProviderAdapter;
import rocks.inspectit.ui.rcp.view.IRefreshableView;
/**
* Abstract Manager view using a {@link TableViewer} as body.
*
* @author Alexander Wert
*
*/
public abstract class AbstractTableBasedManagerView implements CmrRepositoryChangeListener, IRefreshableView {
/**
* {@link FormToolkit}.
*/
protected FormToolkit toolkit;
/**
* {@link SashForm} displayed in the view as main composite.
*/
protected SashForm mainComposite;
/**
* Table viewer in the form.
*/
protected TableViewer tableViewer;
/**
* Main form in upper part of main composite.
*/
protected Form mainForm;
/**
* Help composite for displaying messages.
*/
protected Composite messageComposite;
/**
* {@link CmrRepositoryManager} needed for loading the CMRs.
*/
protected CmrRepositoryManager cmrRepositoryManager;
/**
* Currently displayed {@link CmrRepositoryDefinition}.
*/
protected CmrRepositoryDefinition displayedCmrRepositoryDefinition;
/**
* Cached statuses of CMR repository definitions.
*/
private final Map<CmrRepositoryDefinition, OnlineStatus> cachedOnlineStatus = new ConcurrentHashMap<CmrRepositoryDefinition, OnlineStatus>();
/**
* Adapter to publish the selection to the Site.
*/
protected final SelectionProviderAdapter selectionProviderAdapter = new SelectionProviderAdapter();
/**
* The {@link IWorkbenchPartSite} the view is showed in.
*/
private final IWorkbenchPartSite workbenchPartSite;
/**
* Default Constructor.
*
* @param workbenchPartSite
* The {@link IWorkbenchPartSite} the view is showed in.
*/
public AbstractTableBasedManagerView(IWorkbenchPartSite workbenchPartSite) {
this.workbenchPartSite = workbenchPartSite;
cmrRepositoryManager = InspectIT.getDefault().getCmrRepositoryManager();
cmrRepositoryManager.addCmrRepositoryChangeListener(this);
selectDisplayedCmrRepositoryDefinition();
}
/**
* Indicates whether the passed {@link Object} matches the content type of the corresponding
* {@link AbstractTableBasedManagerView} instance.
*
* @param object
* {@link Object} instance to check
* @return <code>true</code>, if the passed {@link Object} matches the content type of the
* corresponding {@link AbstractTableBasedManagerView} instance. Otherwise
* <code>false</code>.
*/
protected abstract boolean matchesContentType(Object object);
/**
* @return Return label provider for the {@link #tableViewer}.
*/
protected abstract IBaseLabelProvider getLabelProvider();
/**
* @return Returns ID of the menu that should be hooked up to the table viewer.
*/
protected abstract String getMenuId();
/**
* Updates the content of the view.
*/
protected abstract void updateContent();
/**
*
* @return Returns the list of items to be used as input in the table.
*/
protected abstract List<?> getTableInput();
/**
* Creates proper columns based on the selection.
*
* @param tableViewer
* the {@link TableViewer} to build the columns for.
*/
protected abstract void createTableColumns(TableViewer tableViewer);
/**
* Creates the controls of the view.
*
* @param parent
* The parent composite.
* @param multiSelection
* indicates whether the corresponding table should support multi-selection.
*/
public void createControls(Composite parent, boolean multiSelection) {
createViewToolbar();
toolkit = new FormToolkit(parent.getDisplay());
mainComposite = new SashForm(parent, SWT.VERTICAL);
GridLayout mainLayout = new GridLayout(1, true);
mainLayout.marginWidth = 0;
mainLayout.marginHeight = 0;
mainComposite.setLayout(mainLayout);
mainForm = toolkit.createForm(mainComposite);
mainForm.getBody().setLayout(new GridLayout(1, true));
mainForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
createHeadClient(mainForm);
toolkit.decorateFormHeading(mainForm);
int borderStyle = toolkit.getBorderStyle();
toolkit.setBorderStyle(SWT.NULL);
int multiSelectionFlag = multiSelection ? SWT.MULTI : SWT.NONE;
Table table = toolkit.createTable(mainForm.getBody(), SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | multiSelectionFlag);
toolkit.setBorderStyle(borderStyle);
tableViewer = new TableViewer(table);
// create tree content provider
tableViewer.setContentProvider(getContentProvider());
tableViewer.setLabelProvider(getLabelProvider());
ViewerComparator comparator = getViewerComparator();
if (null != comparator) {
tableViewer.setComparator(comparator);
}
ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.NO_RECREATE);
// double-click listener
IDoubleClickListener doubleClickListener = getDoubleClickListener();
if (null != doubleClickListener) {
tableViewer.addDoubleClickListener(doubleClickListener);
}
// menu
String menuId = getMenuId();
if ((null != menuId) && (null != workbenchPartSite)) {
MenuManager menuManager = new MenuManager();
menuManager.setRemoveAllWhenShown(true);
workbenchPartSite.registerContextMenu(menuId, menuManager, tableViewer);
Control control = tableViewer.getControl();
Menu menu = menuManager.createContextMenu(control);
control.setMenu(menu);
}
// resizing listener
mainComposite.addControlListener(new ControlAdapter() {
private boolean verticaLayout;
@Override
public void controlResized(ControlEvent e) {
int width = mainComposite.getBounds().width;
int height = mainComposite.getBounds().height;
if ((width > height) && verticaLayout) {
verticaLayout = false;
mainComposite.setOrientation(SWT.HORIZONTAL);
} else if ((width < height) && !verticaLayout) {
verticaLayout = true;
mainComposite.setOrientation(SWT.VERTICAL);
}
mainComposite.layout();
}
});
// update all
updateFormTitle();
updateFormMenuManager();
updateFormBody();
tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
StructuredSelection ss = (StructuredSelection) tableViewer.getSelection();
if (matchesContentType(ss.getFirstElement())) {
getSelectionProviderAdapter().setSelection(ss);
} else {
// setting selection to the CMR so that we can perform all the necessary
// operations
ISelection selection = (null != displayedCmrRepositoryDefinition) ? new StructuredSelection(displayedCmrRepositoryDefinition) : StructuredSelection.EMPTY;
getSelectionProviderAdapter().setSelection(selection);
}
}
});
if (null != workbenchPartSite) {
workbenchPartSite.setSelectionProvider(getSelectionProviderAdapter());
}
ISelection selection = (null != displayedCmrRepositoryDefinition) ? new StructuredSelection(displayedCmrRepositoryDefinition) : StructuredSelection.EMPTY;
getSelectionProviderAdapter().setSelection(selection);
}
/**
* {@inheritDoc}
*/
@Override
public void repositoryOnlineStatusUpdated(final CmrRepositoryDefinition repositoryDefinition, OnlineStatus oldStatus, OnlineStatus newStatus) {
if (Objects.equals(displayedCmrRepositoryDefinition, repositoryDefinition)) {
OnlineStatus cachedStatus = cachedOnlineStatus.get(repositoryDefinition);
if (newStatus == OnlineStatus.ONLINE) {
if ((null == cachedStatus) || OnlineStatus.OFFLINE.equals(cachedStatus) || OnlineStatus.UNKNOWN.equals(cachedStatus)) {
performUpdate(true);
}
cachedOnlineStatus.put(repositoryDefinition, newStatus);
} else if (newStatus == OnlineStatus.OFFLINE) {
if ((null == cachedStatus) || OnlineStatus.ONLINE.equals(cachedStatus)) {
performUpdate(true);
}
cachedOnlineStatus.put(repositoryDefinition, newStatus);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void repositoryAdded(CmrRepositoryDefinition cmrRepositoryDefinition) {
cachedOnlineStatus.put(cmrRepositoryDefinition, cmrRepositoryDefinition.getOnlineStatus());
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
updateFormMenuManager();
if (null == displayedCmrRepositoryDefinition) {
selectDisplayedCmrRepositoryDefinition();
}
}
}, mainForm);
}
/**
* {@inheritDoc}
*/
@Override
public void repositoryRemoved(final CmrRepositoryDefinition cmrRepositoryDefinition) {
cachedOnlineStatus.remove(cmrRepositoryDefinition);
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
updateFormMenuManager();
// if selected update
if (Objects.equals(displayedCmrRepositoryDefinition, cmrRepositoryDefinition)) {
selectDisplayedCmrRepositoryDefinition();
}
}
}, mainForm);
}
/**
* {@inheritDoc}
*/
@Override
public void repositoryDataUpdated(final CmrRepositoryDefinition cmrRepositoryDefinition) {
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
updateFormMenuManager();
if (Objects.equals(displayedCmrRepositoryDefinition, cmrRepositoryDefinition)) {
updateFormTitle();
}
}
}, mainForm);
}
/**
* {@inheritDoc}
*/
@Override
public void repositoryAgentDeleted(CmrRepositoryDefinition cmrRepositoryDefinition, PlatformIdent agent) {
}
/**
* Informs that the editing repository for the configuration interface has been changed.
*
* @param cmrRepositoryDefinition
* CmrRepositoryDefinition
*/
public void repositoryDefinitionChange(CmrRepositoryDefinition cmrRepositoryDefinition) {
displayedCmrRepositoryDefinition = cmrRepositoryDefinition;
performUpdate(true);
getSelectionProviderAdapter().setSelection(new StructuredSelection(displayedCmrRepositoryDefinition));
}
/**
* Sets the focus to this view.
*/
public void setFocus() {
if (tableViewer.getTable().isVisible()) {
tableViewer.getTable().setFocus();
} else {
mainForm.setFocus();
}
}
/**
* {@inheritDoc}
*/
@Override
public void refresh() {
// check the status of CMR, if it's online do update, if it's offline just fire update CMR
// online status job
if ((null != displayedCmrRepositoryDefinition) && (OnlineStatus.OFFLINE != displayedCmrRepositoryDefinition.getOnlineStatus())) {
performUpdate(true);
} else if (null != displayedCmrRepositoryDefinition) {
InspectIT.getDefault().getCmrRepositoryManager().forceCmrRepositoryOnlineStatusUpdate(displayedCmrRepositoryDefinition);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean canRefresh() {
return true;
}
/**
* Disposes this view and cleans up.
*/
public void dispose() {
cmrRepositoryManager.removeCmrRepositoryChangeListener(this);
}
/**
* Gets {@link #workbenchPartSite}.
*
* @return {@link #workbenchPartSite}
*/
public IWorkbenchPartSite getWorkbenchSite() {
return workbenchPartSite;
}
/**
* Gets {@link #selectionProviderAdapter}.
*
* @return {@link #selectionProviderAdapter}
*/
public SelectionProviderAdapter getSelectionProviderAdapter() {
return selectionProviderAdapter;
}
/**
* Create view tool-bar. Sub-classes can implement if needed.
*/
protected void createViewToolbar() {
}
/**
* Returns the {@link IContentProvider} to be used in the table viewer. The sub-classes can
* override if needed. Default implementation returns the array/collection provider.
*
* @return Returns the {@link IContentProvider} to be used in the table viewer.
*/
protected IContentProvider getContentProvider() {
return new ArrayContentProvider();
}
/**
* Returns comparator to be used in the tree viewer. Default implementation returns a comparator
* based on the {@link Comparable} interface, which means no-comparator.
*
* Sub-class can override.
*
* @return Returns comparator to be used in the tree viewer.
*/
protected ViewerComparator getViewerComparator() {
// just compare based on the comparable interface
return new ViewerComparator() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
if ((e1 instanceof Comparable) && (e2 instanceof Comparable)) {
return ((Comparable) e1).compareTo(e2);
}
return 0;
}
};
}
/**
* @return Returns the {@link IDoubleClickListener} to be activated on the tree double click.
*/
protected IDoubleClickListener getDoubleClickListener() {
return null;
}
/**
* Create head client in the {@link #mainForm}. Sub-classes can implement if needed.
*
* @param form
* form main form.
*/
protected void createHeadClient(Form form) {
}
/**
* Updates the form menu. Sub-classes can extend if needed.
*/
protected void updateFormMenuManager() {
IMenuManager menuManager = mainForm.getMenuManager();
menuManager.removeAll();
for (CmrRepositoryDefinition cmrRepositoryDefinition : cmrRepositoryManager.getCmrRepositoryDefinitions()) {
if (!Objects.equals(cmrRepositoryDefinition, displayedCmrRepositoryDefinition)) {
menuManager.add(new SelectCmrAction(cmrRepositoryDefinition));
}
}
menuManager.update(true);
mainForm.getHead().layout();
}
/**
* Updates the form title. Sub-classes can extend if needed.
*/
protected void updateFormTitle() {
if (null != displayedCmrRepositoryDefinition) {
mainForm.setImage(ImageFormatter.getCmrRepositoryImage(displayedCmrRepositoryDefinition, true));
mainForm.setText(displayedCmrRepositoryDefinition.getName());
mainForm.setToolTipText(TextFormatter.getCmrRepositoryDescription(displayedCmrRepositoryDefinition));
mainForm.setMessage(null);
} else {
mainForm.setImage(null);
mainForm.setText("No repository exists");
mainForm.setMessage("Repositories can be added from the Repository Manager", IMessageProvider.WARNING);
mainForm.setToolTipText(null);
}
}
/**
* Updates the table input and refreshes the table. Sub-classes can extend if needed.
*/
protected void updateFormBody() {
clearFormBody();
if (null == displayedCmrRepositoryDefinition) {
displayMessage("No CMR repository present. Please add the CMR repository via 'Add CMR repository' action.", Display.getDefault().getSystemImage(SWT.ICON_INFORMATION));
} else if (displayedCmrRepositoryDefinition.getOnlineStatus() == OnlineStatus.OFFLINE) {
displayMessage("Selected CMR repository is currently unavailable.", Display.getDefault().getSystemImage(SWT.ICON_WARNING));
} else {
List<?> inputList = getTableInput();
if ((null == inputList) || CollectionUtils.isEmpty(inputList)) {
displayMessage("No items exists on selected CMR repository.", Display.getDefault().getSystemImage(SWT.ICON_INFORMATION));
} else {
createTableColumns(tableViewer);
tableViewer.setLabelProvider(getLabelProvider());
ViewerComparator comparator = getViewerComparator();
if (null != comparator) {
tableViewer.setComparator(getViewerComparator());
}
tableViewer.getTable().setHeaderVisible(true);
tableViewer.getTable().setLinesVisible(true);
tableViewer.getTable().setVisible(true);
tableViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
tableViewer.setInput(inputList);
}
}
mainForm.getBody().layout();
}
/**
* Displays the message on the provided composite.
*
* @param text
* Text of message.
* @param image
* Image to show.
*/
protected void displayMessage(String text, Image image) {
clearFormBody();
if ((null == messageComposite) || messageComposite.isDisposed()) {
messageComposite = toolkit.createComposite(mainForm.getBody());
} else {
for (Control c : messageComposite.getChildren()) {
if (!c.isDisposed()) {
c.dispose();
}
}
}
messageComposite.setLayout(new GridLayout(2, false));
messageComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
toolkit.createLabel(messageComposite, null).setImage(image);
toolkit.createLabel(messageComposite, text, SWT.WRAP).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
mainForm.getBody().layout();
}
/**
* Clears the look of the forms body.
*/
protected void clearFormBody() {
if ((messageComposite != null) && !messageComposite.isDisposed()) {
messageComposite.dispose();
}
tableViewer.setInput(Collections.emptyList());
tableViewer.getTable().setHeaderVisible(false);
tableViewer.getTable().setLinesVisible(false);
tableViewer.getTable().setVisible(false);
tableViewer.getTable().setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
for (TableColumn tableColumn : tableViewer.getTable().getColumns()) {
tableColumn.dispose();
}
}
/**
* Performs an update of the view.
*
* @param updateInput
* Indicates whether the input of this view should be updated as well by accessing
* the CMR.
*/
protected void performUpdate(final boolean updateInput) {
performUpdate(updateInput, null);
}
/**
* Performs an update of the view.
*
* @param updateInput
* Indicates whether the input of this view should be updated as well by accessing
* the CMR. environments.
* @param selection
* Initial selection in the table view. Can be null, in this case no selection is
* applied.
*/
protected void performUpdate(final boolean updateInput, final ISelection selection) {
if (updateInput) {
updateViewContent(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
mainForm.setBusy(true);
updateFormTitle();
updateFormMenuManager();
updateFormBody();
if (null != selection) {
tableViewer.setSelection(selection, true);
}
mainForm.setBusy(false);
mainForm.layout();
}
}, mainForm);
}
});
} else {
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
mainForm.setBusy(true);
updateFormTitle();
updateFormMenuManager();
updateFormBody();
if (null != selection) {
tableViewer.setSelection(selection, true);
}
mainForm.setBusy(false);
mainForm.layout();
}
}, mainForm);
}
}
/**
* Sets selection for the underlying table viewer.
*
* @param selection
* the {@link ISelection} to set.
*/
protected void select(final ISelection selection) {
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
mainForm.setBusy(true);
tableViewer.setSelection(selection, true);
mainForm.setBusy(false);
mainForm.layout();
}
}, mainForm);
}
/**
* Selects the displayed repository definition.
*/
private void selectDisplayedCmrRepositoryDefinition() {
List<CmrRepositoryDefinition> repositories = cmrRepositoryManager.getCmrRepositoryDefinitions();
if (CollectionUtils.isNotEmpty(repositories)) {
// find first online
for (CmrRepositoryDefinition repositoryDefinition : repositories) {
if (repositoryDefinition.getOnlineStatus() == OnlineStatus.ONLINE) {
displayedCmrRepositoryDefinition = repositoryDefinition;
performUpdate(true);
getSelectionProviderAdapter().setSelection(new StructuredSelection(displayedCmrRepositoryDefinition));
return;
}
}
// if no online display first
displayedCmrRepositoryDefinition = repositories.get(0);
performUpdate(true);
getSelectionProviderAdapter().setSelection(new StructuredSelection(displayedCmrRepositoryDefinition));
} else {
// if no CMR available display none
displayedCmrRepositoryDefinition = null; // NOPMD
performUpdate(true);
getSelectionProviderAdapter().setSelection(StructuredSelection.EMPTY);
}
}
/**
* Updates the content of the view by communicating with the CMR.
*
* @param jobListener
* the listener to the job completion, may be <code>null</code>
*/
private void updateViewContent(IJobChangeListener jobListener) {
Job updateStorageListJob = new Job("Update data") {
@Override
protected IStatus run(IProgressMonitor monitor) {
updateContent();
return Status.OK_STATUS;
}
};
if (null != jobListener) {
updateStorageListJob.addJobChangeListener(jobListener);
}
updateStorageListJob.schedule();
}
/**
* Action to select CMR from the form menu.
*
* @author Ivan Senic
*
*/
private class SelectCmrAction extends Action {
/**
* CMR repository to change to.
*/
private final CmrRepositoryDefinition cmrRepositoryDefinition;
/**
* Default constructor.
*
* @param cmrRepositoryDefinition
* {@link CmrRepositoryDefinition}
*/
SelectCmrAction(CmrRepositoryDefinition cmrRepositoryDefinition) {
this.cmrRepositoryDefinition = cmrRepositoryDefinition;
setText(cmrRepositoryDefinition.getName());
setImageDescriptor(ImageDescriptor.createFromImage(ImageFormatter.getCmrRepositoryImage(cmrRepositoryDefinition, true)));
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
repositoryDefinitionChange(cmrRepositoryDefinition);
}
}
}