package rocks.inspectit.ui.rcp.property; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.forms.widgets.FormText; import rocks.inspectit.shared.cs.cmr.property.configuration.PropertySection; import rocks.inspectit.shared.cs.cmr.property.configuration.SingleProperty; import rocks.inspectit.shared.cs.cmr.property.configuration.validation.PropertyValidation; import rocks.inspectit.shared.cs.cmr.property.configuration.validation.ValidationError; import rocks.inspectit.shared.cs.cmr.property.update.AbstractPropertyUpdate; import rocks.inspectit.shared.cs.cmr.property.update.IPropertyUpdate; import rocks.inspectit.ui.rcp.property.control.AbstractPropertyControl; /** * Preference page for displaying the CMR properties. * * @author Ivan Senic * */ public class PropertyPreferencePage extends PreferencePage implements IPropertyUpdateListener { /** * {@link Comparator} to sort properties by being advanced or not. */ private static final Comparator<SingleProperty<?>> PROPERTIES_COMPARATOR = new Comparator<SingleProperty<?>>() { /** * {@inheritDoc} */ @Override public int compare(SingleProperty<?> p1, SingleProperty<?> p2) { if (p1.isAdvanced() && !p2.isAdvanced()) { return 1; } else if (p1.isAdvanced() && !p2.isAdvanced()) { return -1; } else { return 0; } } }; /** * {@link PropertySection} this page will display. */ private List<SingleProperty<?>> properties; /** * All created property controls in this page. */ private Collection<AbstractPropertyControl<?, ?>> propertyControls = new ArrayList<>(); /** * Update map containing all correctly updated properties and their * {@link AbstractPropertyUpdate}. */ protected Map<SingleProperty<?>, IPropertyUpdate<?>> correctUpdateMap = new HashMap<>(); /** * Map of the current validation problems on this page. */ private Map<SingleProperty<?>, PropertyValidation> validationMap = new HashMap<>(); /** * If the page contains all advanced properties. */ private boolean allAdvancedProperties = true; /** * If advanced properties are currently visible. */ private boolean advancedVisible; /** * Button for restoring defaults. */ private Button restoreDefaults; /** * Main composite. */ private Composite mainComposite; /** * For displaying the advanced properties text. */ private FormText advancedText; /** * Default constructor. * * @param name * Name of the page. * @param properties * Collection of properties to display. */ public PropertyPreferencePage(String name, Collection<SingleProperty<?>> properties) { super(name); this.properties = new ArrayList<>(properties); noDefaultAndApplyButton(); for (SingleProperty<?> property : this.properties) { if (!property.isAdvanced()) { allAdvancedProperties = false; } } Collections.sort(this.properties, PROPERTIES_COMPARATOR); } /** * {@inheritDoc} */ @Override public void propertyUpdated(SingleProperty<?> property, IPropertyUpdate<?> propertyUpdate) { validationMap.remove(property); if (!propertyUpdate.isRestoreDefault() || !property.isDefaultValueUsed()) { correctUpdateMap.put(property, propertyUpdate); } else { correctUpdateMap.remove(property); } updatePage(); } /** * {@inheritDoc} */ @Override public void propertyUpdateCanceled(SingleProperty<?> property) { validationMap.remove(property); correctUpdateMap.remove(property); updatePage(); } /** * {@inheritDoc} */ @Override public void propertyValidationFailed(SingleProperty<?> property, PropertyValidation propertyValidation) { if (propertyValidation.hasErrors()) { validationMap.put(property, propertyValidation); correctUpdateMap.remove(property); } updatePage(); } /** * {@inheritDoc} */ @Override public boolean isValid() { return getValidationErrorsCount() == 0; } /** * Returns all valid property updates on this page. Note that this method will return empty * collection if the page is not valid. * * @return Returns all valid property updates on this page. */ public Collection<IPropertyUpdate<?>> getPropertyUpdates() { if (isValid()) { return correctUpdateMap.values(); } else { return Collections.emptySet(); } } /** * If updates made on this page require the server restart. * * @return True if any correct update on this page requires the server restart */ public boolean isServerRestartRequired() { if (isValid()) { for (SingleProperty<?> property : correctUpdateMap.keySet()) { if (property.isServerRestartRequired()) { return true; } } return false; } else { return false; } } /** * Updates the page valid status, message and error table if needed. */ protected void updatePage() { setValid(isValid()); updateMessage(); updateValidationMessages(); } /** * Returns current count of validation errors. Sub-class can override this method to provide * additional errors in the count. * * @return Returns current count of validation errors on this page. */ public int getValidationErrorsCount() { if (MapUtils.isEmpty(validationMap)) { return 0; } else { int count = 0; for (PropertyValidation propertyValidation : validationMap.values()) { count += propertyValidation.getErrorCount(); } return count; } } /** * Returns all validation errors. Sub-class can override this method to provide additional * errors. * * @return Returns current validation errors on this page. */ public Collection<ValidationError> getValidationErrors() { if (MapUtils.isEmpty(validationMap)) { return Collections.emptyList(); } else { List<ValidationError> returnList = new ArrayList<>(); for (PropertyValidation propertyValidation : validationMap.values()) { returnList.addAll(propertyValidation.getErrors()); } return returnList; } } /** * Shows/hides advanced properties and it's controls. * * @param advanced * True if advanced should be shown, false otherwise. */ public void showAdvanced(boolean advanced) { advancedVisible = advanced; if (null != advancedText) { advancedText.setVisible(advanced); } if (CollectionUtils.isNotEmpty(propertyControls)) { for (AbstractPropertyControl<?, ?> propertyControl : propertyControls) { propertyControl.showIfAdvanced(advanced); } mainComposite.layout(); mainComposite.update(); if (allAdvancedProperties && !advancedVisible) { setMessage("This page contains only advanced properties.", INFORMATION); } else { updateMessage(); } } } /** * Updates the message of the page based on number of validation errors. */ private void updateMessage() { int count = getValidationErrorsCount(); if (0 == count) { setMessage(null); } else { String msg = count + " validation error" + (count > 1 ? "s" : "") + " detected"; setMessage(msg, ERROR); } } /** * Updates the error composite. */ private void updateValidationMessages() { if (!mainComposite.isDisposed()) { for (AbstractPropertyControl<?, ?> propertyControl : propertyControls) { propertyControl.displayValidationErrors(getValidationErrors()); } } } /** * {@inheritDoc} */ @Override protected void performDefaults() { for (AbstractPropertyControl<?, ?> control : propertyControls) { control.restoreDefault(); } } /** * {@inheritDoc} * <p> * We hook here the show advanced and our own restore defaults button. */ @Override protected void contributeButtons(Composite parent) { ((GridLayout) parent.getLayout()).numColumns++; restoreDefaults = new Button(parent, SWT.PUSH); restoreDefaults.setText("Restore Defaults"); restoreDefaults.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { performDefaults(); } }); } /** * {@inheritDoc} */ @Override protected Control createContents(Composite parent) { mainComposite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(4, false); layout.marginHeight = 0; layout.marginWidth = 0; mainComposite.setLayout(layout); boolean advancedCreated = false; for (SingleProperty<?> property : properties) { if (!advancedCreated && property.isAdvanced()) { advancedText = new FormText(mainComposite, SWT.WRAP); advancedText.setText("<form><p><b>Advanced properties:</b></p></form>", true, false); advancedText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 4, 1)); advancedCreated = true; } createSinglePropertyContents(property, mainComposite); } Dialog.applyDialogFont(mainComposite); showAdvanced(advancedVisible); mainComposite.layout(); return mainComposite; } /** * Creates one line of widgets in the page for displaying a single property. * * @param property * {@link SingleProperty} to create content for. * @param parent * Composite parent */ private void createSinglePropertyContents(SingleProperty<?> property, Composite parent) { AbstractPropertyControl<?, ?> propertyControl = AbstractPropertyControl.createFor(property, this); propertyControl.create(parent); propertyControls.add(propertyControl); } /** * Gets {@link #allAdvancedProperties}. * * @return {@link #allAdvancedProperties} */ public boolean isAllAdvancedProperties() { return allAdvancedProperties; } }