package rocks.inspectit.ui.rcp.property.control;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import rocks.inspectit.shared.cs.cmr.property.configuration.SingleProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.BooleanProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.ByteProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.LongProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.PercentageProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.StringProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.PropertyValidation;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.PropertyValidationException;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.ValidationError;
import rocks.inspectit.shared.cs.cmr.property.update.IPropertyUpdate;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.InspectITImages;
import rocks.inspectit.ui.rcp.property.IPropertyUpdateListener;
import rocks.inspectit.ui.rcp.property.control.impl.BooleanPropertyControl;
import rocks.inspectit.ui.rcp.property.control.impl.BytePropertyControl;
import rocks.inspectit.ui.rcp.property.control.impl.LongPropertyControl;
import rocks.inspectit.ui.rcp.property.control.impl.PercentagePropertyControl;
import rocks.inspectit.ui.rcp.property.control.impl.StringPropertyControl;
/**
* Abstract class for all property controls.
*
* @author Ivan Senic
*
* @param <E>
* Type of the property control can handle.
* @param <V>
* Value holding the property.
*/
public abstract class AbstractPropertyControl<E extends SingleProperty<V>, V> {
/**
* Property.
*/
protected E property;
/**
* If property gets changed, we will keep here the propertyUpdate.
*/
protected IPropertyUpdate<V> propertyUpdate;
/**
* Property update listener to report updates to.
*/
protected IPropertyUpdateListener propertyUpdateListener;
/**
* Decoration for the mail control for displaying validation errors.
*/
protected ControlDecoration decoration;
/**
* If decoration should be displayed.
*/
private boolean decorationDisplayed = false;
/**
* Main control that displays the value and enables updates.
*/
private Control control;
/**
* All controls, needed for hiding in case of advanced properties.
*/
private List<Control> allControls = new ArrayList<>();
/**
* Default constructor.
*
* @param property
* Property.
* @param propertyUpdateListener
* Property update listener to report updates to.
*/
protected AbstractPropertyControl(E property, IPropertyUpdateListener propertyUpdateListener) {
this.property = property;
this.propertyUpdateListener = propertyUpdateListener;
}
/**
* Create control that will be displayed for the property.
*
* @param parent
* Parent composite.
* @return Created control.
*/
protected abstract Control createControl(Composite parent);
/**
* Shows default value in the control.
*/
protected abstract void showDefaultValue();
/**
* Shows or hides all controls for this property if property is advanced.
*
* @param visible
* <code>true</code> if control should be visible, <code>false</code> otherwise.
*/
public void showIfAdvanced(boolean visible) {
if (property.isAdvanced()) {
for (Control control : allControls) {
control.setVisible(visible);
}
if (decorationDisplayed && visible) {
decoration.show();
} else {
decoration.hide();
}
}
}
/**
* Restores default value in this property control.
*/
public void restoreDefault() {
this.showDefaultValue();
propertyUpdate = property.createRestoreDefaultPropertyUpdate();
propertyUpdateListener.propertyUpdated(property, propertyUpdate);
}
/**
* Displays validation errors in the decoration box if any of the passed {@link ValidationError}
* s is involving the property displayed in this control. Otherwise the decoration box is
* hidden.
*
* @param errors
* Validation errors to check.
*/
public void displayValidationErrors(Collection<ValidationError> errors) {
boolean showDecoration = false;
StringBuilder stringBuilder = new StringBuilder("Validation errors:");
for (ValidationError error : errors) {
if (error.getInvolvedProperties().contains(property)) {
showDecoration = true;
stringBuilder.append("\n - ");
stringBuilder.append(error.getMessage());
}
}
if (showDecoration) {
decoration.show();
decoration.setDescriptionText(stringBuilder.toString());
} else {
decoration.hide();
}
decorationDisplayed = showDecoration;
}
/**
* Creates set of controls that will be displayed for the property.
* <p>
* It is expected that the parent composite has a grid layout with four columns.
*
* @param parent
* Parent composite.
*/
public synchronized void create(Composite parent) {
if (null == control) {
// name first
Label name = new Label(parent, SWT.LEFT);
name.setText(property.getName() + ":");
name.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
// then specific control
control = createControl(parent);
GridData controlGridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
controlGridData.widthHint = 175;
controlGridData.horizontalIndent = 10;
control.setLayoutData(controlGridData);
// decoration for handling validation errors
decoration = new ControlDecoration(control, SWT.LEFT | SWT.BOTTOM);
decoration.setImage(FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage());
decoration.hide();
// info icon
Label info = new Label(parent, SWT.CENTER);
info.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_INFORMATION));
info.setToolTipText(property.getDescription());
info.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
// server re-start icon if needed
Label serverRestart = new Label(parent, SWT.CENTER);
if (property.isServerRestartRequired()) {
serverRestart.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_WARNING));
serverRestart.setToolTipText("Changing this property requires restart of the CMR");
}
serverRestart.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
CollectionUtils.addAll(allControls, new Object[] { name, control, info, serverRestart });
}
}
/**
* Sends property update event.
*
* @param newValue
* New value.
*/
protected void sendPropertyUpdateEvent(V newValue) {
if (!property.getValue().equals(newValue)) {
try {
propertyUpdate = property.createAndValidatePropertyUpdate(newValue);
propertyUpdateListener.propertyUpdated(property, propertyUpdate);
} catch (PropertyValidationException e) {
PropertyValidation propertyValidation = e.getPropertyValidation();
propertyUpdateListener.propertyValidationFailed(property, propertyValidation);
}
} else {
propertyUpdateListener.propertyUpdateCanceled(property);
propertyUpdate = null; // NOPMD
}
}
/**
* @return Returns the last correctly set value for the property.
*/
protected V getLastCorrectValue() {
if (null != propertyUpdate) {
return propertyUpdate.getUpdateValue();
} else {
return property.getValue();
}
}
/**
* Utility method for creating the {@link AbstractPropertyControl}.
*
* @param property
* Property to get the control for.
* @param propertyUpdateListener
* Property update listener to report updates to.
* @return Returns {@link AbstractPropertyControl} or <code>null</code> if the one for the given
* property can not be created.
*/
public static AbstractPropertyControl<?, ?> createFor(SingleProperty<?> property, IPropertyUpdateListener propertyUpdateListener) {
if (property instanceof BooleanProperty) {
return new BooleanPropertyControl((BooleanProperty) property, propertyUpdateListener);
} else if (property instanceof PercentageProperty) {
return new PercentagePropertyControl((PercentageProperty) property, propertyUpdateListener);
} else if (property instanceof StringProperty) {
return new StringPropertyControl((StringProperty) property, propertyUpdateListener);
} else if (property instanceof LongProperty) {
return new LongPropertyControl((LongProperty) property, propertyUpdateListener);
} else if (property instanceof ByteProperty) {
return new BytePropertyControl((ByteProperty) property, propertyUpdateListener);
}
return null;
}
}