package rocks.inspectit.ui.rcp.wizard.page;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import rocks.inspectit.shared.all.util.ObjectUtils;
import rocks.inspectit.shared.cs.storage.StorageData;
import rocks.inspectit.shared.cs.storage.label.AbstractStorageLabel;
import rocks.inspectit.shared.cs.storage.label.BooleanStorageLabel;
import rocks.inspectit.shared.cs.storage.label.DateStorageLabel;
import rocks.inspectit.shared.cs.storage.label.NumberStorageLabel;
import rocks.inspectit.shared.cs.storage.label.StringStorageLabel;
import rocks.inspectit.shared.cs.storage.label.type.AbstractStorageLabelType;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.InspectITImages;
import rocks.inspectit.ui.rcp.editor.viewers.StyledCellIndexLabelProvider;
import rocks.inspectit.ui.rcp.formatter.ImageFormatter;
import rocks.inspectit.ui.rcp.formatter.TextFormatter;
import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition;
import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition.OnlineStatus;
import rocks.inspectit.ui.rcp.storage.label.edit.LabelValueEditingSupport;
import rocks.inspectit.ui.rcp.storage.label.edit.LabelValueEditingSupport.LabelEditListener;
import rocks.inspectit.ui.rcp.util.SafeExecutor;
import rocks.inspectit.ui.rcp.wizard.ManageLabelWizard;
/**
* Page for adding storage labels.
*
* @author Ivan Senic
*
*/
public class AddStorageLabelWizardPage extends WizardPage {
/**
* {@link StyledString} that has empty string.
*/
protected static final StyledString EMPTY_STYLED_STRING = new StyledString();
/**
* Defines if the data needed to be entered on this wizard page is optional. If it is optional
* the page {@link #isPageComplete()} method will return true always, so that wizards can finish
* although no label was inserted.
*/
private final boolean optional;
/**
* Default message.
*/
private String defaultMessage = "Define the new label type and its value";
/**
* {@link StorageData} to add label to.
*/
private StorageData storageData;
/**
* {@link CmrRepositoryDefinition} where data is located.
*/
private final CmrRepositoryDefinition cmrRepositoryDefinition;
/**
* List of available labels types.
*/
private List<AbstractStorageLabelType<?>> labelTypeList;
/**
* List of labels to add.
*/
private final List<AbstractStorageLabel<?>> labelsToAdd = new ArrayList<>();
/**
* Widgets.
*/
private Combo labelTypeSelection;
/**
* Table to display labels.
*/
private TableViewer labelsTableViewer;
/**
* Main composite.
*/
private Composite main;
/**
* Remove label button.
*/
private Button removeLabelButton;
/**
* Page complete listener.
*/
private final Listener pageCompleteListener = new Listener() {
@Override
public void handleEvent(Event event) {
setPageComplete(isPageComplete());
}
};
/**
* Value column.
*/
private TableViewerColumn value;
/**
* Default constructor. Use this constructor when storage data is known. Note that using the
* page this way, {@link #optional} value is set to true, thus wizard page would not allow to
* finish until label is correctly created.
*
* @param storageData
* {@link StorageData}
* @param cmrRepositoryDefinition
* {@link CmrRepositoryDefinition}
*/
public AddStorageLabelWizardPage(StorageData storageData, CmrRepositoryDefinition cmrRepositoryDefinition) {
super("Add New Labels");
this.setTitle("Add New Labels");
defaultMessage += " for the storage '" + storageData.getName() + "'";
this.setMessage(defaultMessage);
this.storageData = storageData;
this.cmrRepositoryDefinition = cmrRepositoryDefinition;
this.optional = false;
labelTypeList = cmrRepositoryDefinition.getStorageService().getAllLabelTypes();
}
/**
* The constructor for usage when storage to label is not known in advance. Note that using this
* constructor will set {@link #optional} to true.
*
* @param cmrRepositoryDefinition
* {@link CmrRepositoryDefinition}
*/
public AddStorageLabelWizardPage(CmrRepositoryDefinition cmrRepositoryDefinition) {
super("Add New Labels");
this.setTitle("Add New Labels");
defaultMessage = "Optionally add one or more labels to the selected storage";
this.setMessage(defaultMessage);
this.cmrRepositoryDefinition = cmrRepositoryDefinition;
this.optional = true;
}
/**
* {@inheritDoc}
*/
@Override
public void createControl(Composite parent) {
main = new Composite(parent, SWT.NONE);
main.setLayout(new GridLayout(4, false));
Label l = new Label(main, SWT.NONE);
l.setText("Label type:");
l.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
labelTypeSelection = new Combo(main, SWT.READ_ONLY);
labelTypeSelection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final Button addButton = new Button(main, SWT.PUSH);
addButton.setEnabled(false);
addButton.setText("Add");
addButton.setToolTipText("Add Label");
addButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
addButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("unchecked")
@Override
public void widgetSelected(SelectionEvent e) {
int index = labelTypeSelection.getSelectionIndex();
if (-1 != index) {
AbstractStorageLabelType<?> selectedLabelType = labelTypeList.get(index);
if (selectedLabelType.isOnePerStorage() && isLabelTypePresent(selectedLabelType)) {
setMessage("Selected label type is one-per-storage. New label value will overwrite the old one.", IMessageProvider.WARNING);
Iterator<AbstractStorageLabel<?>> it = labelsToAdd.iterator();
while (it.hasNext()) {
if (Objects.equals(it.next().getStorageLabelType(), selectedLabelType)) {
it.remove();
break;
}
}
} else {
setMessage(defaultMessage);
}
if (selectedLabelType.getValueClass().equals(Boolean.class)) {
BooleanStorageLabel booleanStorageLabel = new BooleanStorageLabel();
booleanStorageLabel.setStorageLabelType((AbstractStorageLabelType<Boolean>) selectedLabelType);
labelsToAdd.add(booleanStorageLabel);
} else if (selectedLabelType.getValueClass().equals(Date.class)) {
DateStorageLabel dateStorageLabel = new DateStorageLabel();
dateStorageLabel.setStorageLabelType((AbstractStorageLabelType<Date>) selectedLabelType);
labelsToAdd.add(dateStorageLabel);
} else if (selectedLabelType.getValueClass().equals(Number.class)) {
NumberStorageLabel numberStorageLabel = new NumberStorageLabel();
numberStorageLabel.setStorageLabelType((AbstractStorageLabelType<Number>) selectedLabelType);
labelsToAdd.add(numberStorageLabel);
} else if (selectedLabelType.getValueClass().equals(String.class)) {
StringStorageLabel stringStorageLabel = new StringStorageLabel();
stringStorageLabel.setStorageLabelType((AbstractStorageLabelType<String>) selectedLabelType);
labelsToAdd.add(stringStorageLabel);
}
labelsTableViewer.refresh();
labelsTableViewer.editElement(labelsToAdd.get(labelsToAdd.size() - 1), 1);
}
}
});
labelTypeSelection.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
addButton.setEnabled(labelTypeSelection.getSelectionIndex() >= 0);
}
});
Button manageLabelsButton = new Button(main, SWT.PUSH);
manageLabelsButton.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_LABEL));
manageLabelsButton.setToolTipText("Manage Labels on Repository");
manageLabelsButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
manageLabelsButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ManageLabelWizard manageLabelWizard = new ManageLabelWizard(cmrRepositoryDefinition);
WizardDialog wizardDialog = new WizardDialog(getShell(), manageLabelWizard);
wizardDialog.open();
if (wizardDialog.getReturnCode() == Window.OK) {
updateLabelTypes();
}
}
});
// table
createLabelTable(main);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1);
gd.minimumHeight = 100;
labelsTableViewer.getTable().setLayoutData(gd);
addButton.addListener(SWT.Selection, pageCompleteListener);
labelsTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
removeLabelButton.setEnabled(!labelsTableViewer.getSelection().isEmpty());
}
});
labelsTableViewer.getTable().addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.DEL) {
deleteSelectedlabels();
}
}
});
// empty help label
gd = new GridData();
gd.horizontalSpan = 2;
new Label(main, SWT.NONE).setLayoutData(gd);
removeLabelButton = new Button(main, SWT.PUSH);
removeLabelButton.setText("Remove");
removeLabelButton.setToolTipText("Remove Selected Labels");
removeLabelButton.setEnabled(false);
removeLabelButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1));
removeLabelButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
deleteSelectedlabels();
}
});
updateLabelTypes();
setControl(main);
}
/**
* Updates the labels types available on the server.
*
*/
private void updateLabelTypes() {
Job updateLabelsTypes = new Job("Loading Label Types") {
@Override
protected IStatus run(IProgressMonitor monitor) {
if (cmrRepositoryDefinition.getOnlineStatus() != OnlineStatus.OFFLINE) {
labelTypeList = cmrRepositoryDefinition.getStorageService().getAllLabelTypes();
} else {
labelTypeList = Collections.emptyList();
}
SafeExecutor.asyncExec(new Runnable() {
@Override
public void run() {
labelTypeSelection.removeAll();
for (AbstractStorageLabelType<?> labelType : labelTypeList) {
labelTypeSelection.add(TextFormatter.getLabelName(labelType));
}
}
}, labelTypeSelection);
return Status.OK_STATUS;
}
};
updateLabelsTypes.schedule();
}
/**
* Deletes selected labels.
*/
private void deleteSelectedlabels() {
StructuredSelection structuredSelection = (StructuredSelection) labelsTableViewer.getSelection();
if (!structuredSelection.isEmpty()) {
for (Iterator<?> it = structuredSelection.iterator(); it.hasNext();) {
labelsToAdd.remove(it.next());
}
labelsTableViewer.refresh();
setPageComplete(isPageComplete());
}
}
/**
* Creates the table for the labels.
*
* @param parent
* Parent composite.
*/
private void createLabelTable(Composite parent) {
Table table = new Table(parent, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
labelsTableViewer = new TableViewer(table);
TableViewerColumn type = new TableViewerColumn(labelsTableViewer, SWT.NONE);
type.getColumn().setText("Label");
type.getColumn().setMoveable(false);
type.getColumn().setResizable(true);
type.getColumn().setWidth(200);
value = new TableViewerColumn(labelsTableViewer, SWT.NONE);
value.getColumn().setText("Value");
value.getColumn().setMoveable(false);
value.getColumn().setResizable(true);
value.getColumn().setWidth(100);
LabelValueEditingSupport editingSupport = new LabelValueEditingSupport(labelsTableViewer, storageData, cmrRepositoryDefinition);
editingSupport.addLabelEditListener(new LabelEditListener() {
@Override
public void preLabelValueChange(AbstractStorageLabel<?> label) {
}
@Override
public void postLabelValueChange(AbstractStorageLabel<?> label) {
setPageComplete(isPageComplete());
}
});
value.setEditingSupport(editingSupport);
labelsTableViewer.setContentProvider(new ArrayContentProvider());
labelsTableViewer.setLabelProvider(new StyledCellIndexLabelProvider() {
@Override
protected StyledString getStyledText(Object element, int index) {
if (element instanceof AbstractStorageLabel) {
AbstractStorageLabel<?> label = (AbstractStorageLabel<?>) element;
switch (index) {
case 0:
return new StyledString(TextFormatter.getLabelName(label));
case 1:
if (null != label.getValue()) {
return new StyledString(TextFormatter.getLabelValue(label, false));
}
default:
break;
}
}
return EMPTY_STYLED_STRING;
}
@Override
protected Image getColumnImage(Object element, int index) {
if ((index == 0) && (element instanceof AbstractStorageLabel)) {
return ImageFormatter.getImageForLabel(((AbstractStorageLabel<?>) element).getStorageLabelType());
}
return null;
}
});
labelsTableViewer.setInput(labelsToAdd);
}
/**
* Returns if the label type is present in the storage or in the list of labels to add.
*
* @param selectedLabelType
* Selected {@link AbstractStorageLabelType}.
* @return True if label type already exists.
*/
private boolean isLabelTypePresent(AbstractStorageLabelType<?> selectedLabelType) {
if (storageData.isLabelPresent(selectedLabelType)) {
return true;
}
for (AbstractStorageLabel<?> label : labelsToAdd) {
if (ObjectUtils.equals(selectedLabelType, label.getStorageLabelType())) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPageComplete() {
if (optional) {
return true;
} else if (!labelsToAdd.isEmpty()) {
for (AbstractStorageLabel<?> label : labelsToAdd) {
if (null != label.getValue()) {
return true;
}
}
}
return false;
}
/**
* Gets {@link #labelsToAdd}.
*
* @return {@link #labelsToAdd}
*/
public List<AbstractStorageLabel<?>> getLabelsToAdd() {
Iterator<AbstractStorageLabel<?>> it = labelsToAdd.iterator();
while (it.hasNext()) {
if (null == it.next().getValue()) {
it.remove();
}
}
return labelsToAdd;
}
/**
* Sets {@link #storageData}.
*
* @param storageData
* New value for {@link #storageData}
*/
public void setStorageData(StorageData storageData) {
this.storageData = storageData;
// update the editing support
LabelValueEditingSupport editingSupport = new LabelValueEditingSupport(labelsTableViewer, storageData, cmrRepositoryDefinition);
editingSupport.addLabelEditListener(new LabelEditListener() {
@Override
public void preLabelValueChange(AbstractStorageLabel<?> label) {
}
@Override
public void postLabelValueChange(AbstractStorageLabel<?> label) {
setPageComplete(isPageComplete());
}
});
value.setEditingSupport(editingSupport);
}
}