/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.coregui.client.components.configuration;
import static com.smartgwt.client.types.Overflow.VISIBLE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.AutoFitWidthApproach;
import com.smartgwt.client.types.Autofit;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.MultipleAppearance;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.VisibilityMode;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.util.ValueCallback;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.ValuesManager;
import com.smartgwt.client.widgets.form.events.ItemChangedEvent;
import com.smartgwt.client.widgets.form.events.ItemChangedHandler;
import com.smartgwt.client.widgets.form.fields.ButtonItem;
import com.smartgwt.client.widgets.form.fields.CanvasItem;
import com.smartgwt.client.widgets.form.fields.CheckboxItem;
import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
import com.smartgwt.client.widgets.form.fields.FloatItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.PasswordItem;
import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.SpacerItem;
import com.smartgwt.client.widgets.form.fields.SpinnerItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.form.fields.TextAreaItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.form.fields.events.FocusEvent;
import com.smartgwt.client.widgets.form.fields.events.FocusHandler;
import com.smartgwt.client.widgets.form.validator.CustomValidator;
import com.smartgwt.client.widgets.form.validator.FloatRangeValidator;
import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator;
import com.smartgwt.client.widgets.form.validator.IsFloatValidator;
import com.smartgwt.client.widgets.form.validator.IsIntegerValidator;
import com.smartgwt.client.widgets.form.validator.LengthRangeValidator;
import com.smartgwt.client.widgets.form.validator.RegExpValidator;
import com.smartgwt.client.widgets.form.validator.Validator;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.SectionStack;
import com.smartgwt.client.widgets.layout.SectionStackSection;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.menu.IMenuButton;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuItem;
import com.smartgwt.client.widgets.menu.MenuItemSeparator;
import com.smartgwt.client.widgets.menu.events.ClickHandler;
import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import org.rhq.core.domain.configuration.AbstractPropertyMap;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.ConfigurationFormat;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionEnumeration;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionList;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertyGroupDefinition;
import org.rhq.core.domain.configuration.definition.PropertySimpleType;
import org.rhq.core.domain.configuration.definition.constraint.Constraint;
import org.rhq.core.domain.configuration.definition.constraint.FloatRangeConstraint;
import org.rhq.core.domain.configuration.definition.constraint.IntegerRangeConstraint;
import org.rhq.core.domain.configuration.definition.constraint.RegexConstraint;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.ImageManager;
import org.rhq.coregui.client.components.form.IsLongValidator;
import org.rhq.coregui.client.components.form.SortedSelectItem;
import org.rhq.coregui.client.gwt.ConfigurationGWTServiceAsync;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.StringUtility;
import org.rhq.coregui.client.util.enhanced.EnhancedHLayout;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor;
import org.rhq.coregui.client.util.enhanced.EnhancedToolStrip;
import org.rhq.coregui.client.util.enhanced.EnhancedVLayout;
import org.rhq.coregui.client.util.message.Message;
/**
* A SmartGWT widget for editing an RHQ {@link Configuration} that conforms to a {@link ConfigurationDefinition}.
*
* @author Greg Hinkle
* @author Ian Springer
*/
//
// Note: There was a failed attempt at an editor composed with ListGrid components instead of DynamicForm components,
// but there were problems with having different editors active for different rows in the table at the same time.
// Smart says they're working on enhancing this area, but the DynamicForm might be a better option anyway. (ghinkle)
//
public class ConfigurationEditor extends EnhancedVLayout {
static final LinkedHashMap<String, String> BOOLEAN_PROPERTY_ITEM_VALUE_MAP = new LinkedHashMap<String, String>();
static {
BOOLEAN_PROPERTY_ITEM_VALUE_MAP.put(Boolean.TRUE.toString(), MSG.common_val_yes());
BOOLEAN_PROPERTY_ITEM_VALUE_MAP.put(Boolean.FALSE.toString(), MSG.common_val_no());
}
private ConfigurationGWTServiceAsync configurationService = GWTServiceLookup.getConfigurationService();
private EnhancedToolStrip toolStrip;
private ConfigurationDefinition configurationDefinition;
private Configuration configuration;
private Configuration originalConfiguration;
private ValuesManager topLevelPropertiesValuesManager = new ValuesManager();
private ValuesManager mapLevelPropertiesValuesManager = new ValuesManager();
private Label loadingLabel = new Label(MSG.common_msg_loading());
private int resourceId;
private int resourceTypeId;
private ConfigType configType;
private String editorTitle = null;
private boolean readOnly = false;
private boolean allPropertiesWritable = false;
private boolean preserveTextFormatting = false;
private Map<String, String> invalidPropertyNameToDisplayNameMap = new HashMap<String, String>();
private Set<PropertyValueChangeListener> propertyValueChangeListeners = new HashSet<PropertyValueChangeListener>();
private FormItem blurValueItem;
private CheckboxItem blurUnsetItem;
private HashMap<PropertyDefinitionList, ListGrid> listOfMapsGrids = new HashMap<PropertyDefinitionList, ListGrid>();
private HashMap<String, PropertyDefinitionMap> formPropertyDefinitionMapMap = new HashMap<String, PropertyDefinitionMap>();
public static enum ConfigType {
plugin, resource
}; // Need this extra semicolon for the qdox parser
/**
* This is the kind of handler that is called when this editor has loaded
* the configuration and configuration definition. If the load failed,
* the methods will still be called, but will be passed null.
*/
public static interface LoadHandler {
void loadedConfiguration(Configuration config);
void loadedConfigurationDefinition(ConfigurationDefinition configDef);
}
private LoadHandler loadHandler = null;
public ConfigurationEditor(int resourceId, int resourceTypeId, ConfigType configType) {
this.resourceId = resourceId;
this.resourceTypeId = resourceTypeId;
this.configType = configType;
}
public ConfigurationEditor(ConfigurationDefinition configurationDefinition, Configuration configuration) {
if (configuration == null) {
throw new IllegalArgumentException("Null configuration.");
}
if (configurationDefinition == null) {
throw new IllegalArgumentException("Null configurationDefinition.");
}
this.configuration = configuration;
this.configurationDefinition = configurationDefinition;
}
public void setLoadHandler(LoadHandler handler) {
this.loadHandler = handler;
}
public Configuration getConfiguration() {
return configuration;
}
public ConfigurationDefinition getConfigurationDefinition() {
return configurationDefinition;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public boolean isAllPropertiesWritable() {
return allPropertiesWritable;
}
public void setAllPropertiesWritable(boolean allPropertiesWritable) {
this.allPropertiesWritable = allPropertiesWritable;
}
public boolean isPreserveTextFormatting() {
return preserveTextFormatting;
}
public void setPreserveTextFormatting(boolean preserveFormatting) {
this.preserveTextFormatting = preserveFormatting;
}
public String getEditorTitle() {
return editorTitle;
}
public void setEditorTitle(String title) {
this.editorTitle = title;
}
public void showError(Throwable failure) {
addMember(new Label(failure.getMessage()));
}
public void showError(String message) {
addMember(new Label(message));
}
public boolean validate() {
return this.topLevelPropertiesValuesManager.validate() && this.mapLevelPropertiesValuesManager.validate()
&& listOfMapsGridsAreValid();
}
private boolean listOfMapsGridsAreValid() {
for (Map.Entry<PropertyDefinitionList, ListGrid> entry : listOfMapsGrids.entrySet()) {
PropertyDefinitionList propertyDefinitionList = entry.getKey();
int listMin = propertyDefinitionList.getMin();
int listMax = propertyDefinitionList.getMax();
ListGridRecord[] gridRecords = entry.getValue().getRecords();
if (!isListGridRecordCountValid(gridRecords, listMin, listMax)) {
return false;
}
}
return true;
}
private boolean isListGridRecordCountValid(ListGridRecord[] gridRecords, int min, int max) {
int gridRecordCount = gridRecords == null ? 0 : gridRecords.length;
if (gridRecordCount < min || gridRecordCount > max) {
return false;
}
return true;
}
public boolean isValid() {
return !this.topLevelPropertiesValuesManager.hasErrors() && !this.mapLevelPropertiesValuesManager.hasErrors()
&& listOfMapsGridsAreValid();
}
public void addPropertyValueChangeListener(PropertyValueChangeListener propertyValueChangeListener) {
this.propertyValueChangeListeners.add(propertyValueChangeListener);
}
@Override
protected void onDraw() {
super.onDraw();
addMember(loadingLabel);
this.redraw();
final long start = System.currentTimeMillis();
if (configurationDefinition == null || configuration == null) {
if (configType == ConfigType.resource) {
loadResourceConfiguration(start);
} else if (configType == ConfigType.plugin) {
loadPluginConfiguration(start);
}
}
reload();
}
private void loadPluginConfiguration(final long start) {
configurationService.getPluginConfiguration(resourceId, new AsyncCallback<Configuration>() {
public void onFailure(Throwable caught) {
showError(caught);
if (loadHandler != null) {
loadHandler.loadedConfiguration(null);
}
}
public void onSuccess(Configuration result) {
configuration = result;
reload();
if (loadHandler != null) {
loadHandler.loadedConfiguration(configuration);
}
}
});
ResourceTypeRepository.Cache.getInstance().getResourceTypes(new Integer[] { resourceTypeId },
EnumSet.of(ResourceTypeRepository.MetadataType.pluginConfigurationDefinition),
new ResourceTypeRepository.TypesLoadedCallback() {
public void onTypesLoaded(Map<Integer, ResourceType> types) {
Log.debug("ConfigDef retrieved in: " + (System.currentTimeMillis() - start));
configurationDefinition = types.get(resourceTypeId).getPluginConfigurationDefinition();
if (configurationDefinition == null) {
showError(MSG.view_configEdit_error_2());
}
reload();
if (loadHandler != null) {
loadHandler.loadedConfigurationDefinition(configurationDefinition);
}
}
});
}
private void loadResourceConfiguration(final long start) {
configurationService.getResourceConfiguration(resourceId, new AsyncCallback<Configuration>() {
public void onFailure(Throwable caught) {
showError(caught);
if (loadHandler != null) {
loadHandler.loadedConfiguration(null);
}
}
public void onSuccess(Configuration result) {
configuration = result;
Log.info("Config retrieved in: " + (System.currentTimeMillis() - start));
reload();
if (loadHandler != null) {
loadHandler.loadedConfiguration(configuration);
}
}
});
ResourceTypeRepository.Cache.getInstance().getResourceTypes(new Integer[] { resourceTypeId },
EnumSet.of(ResourceTypeRepository.MetadataType.resourceConfigurationDefinition),
new ResourceTypeRepository.TypesLoadedCallback() {
public void onTypesLoaded(Map<Integer, ResourceType> types) {
Log.debug("ConfigDef retrieved in: " + (System.currentTimeMillis() - start));
configurationDefinition = types.get(resourceTypeId).getResourceConfigurationDefinition();
if (configurationDefinition == null) {
loadingLabel.hide();
showError(MSG.view_configEdit_error_1());
}
reload();
if (loadHandler != null) {
loadHandler.loadedConfigurationDefinition(configurationDefinition);
}
}
});
}
public void reload() {
if (this.configurationDefinition == null || this.configuration == null) {
// Wait for both to load.
return;
}
if (this.originalConfiguration == null) {
this.originalConfiguration = configuration.deepCopy();
}
String oobProp = this.configuration.getSimpleValue("__OOB", null);
if (oobProp != null) {
Message msg = new Message(oobProp, Message.Severity.Warning, EnumSet.of(Message.Option.Transient));
CoreGUI.getMessageCenter().notify(msg);
}
for (Canvas childCanvas : getChildren()) {
childCanvas.destroy();
}
if (configurationDefinition.getConfigurationFormat() == ConfigurationFormat.STRUCTURED
|| configurationDefinition.getConfigurationFormat() == ConfigurationFormat.STRUCTURED_AND_RAW) {
Log.debug("Building structured configuration editor...");
EnhancedVLayout structuredConfigLayout = buildStructuredPane();
addMember(structuredConfigLayout);
} else {
Label label = new Label("Structured configuration is not supported.");
addMember(label);
}
this.markForRedraw();
}
public void reset() {
this.configuration = this.originalConfiguration;
this.originalConfiguration = null; // so reload gets another copy
reload();
}
protected EnhancedVLayout buildStructuredPane() {
EnhancedVLayout layout = new EnhancedVLayout();
List<PropertyGroupDefinition> groupDefinitions = configurationDefinition.getGroupDefinitions();
if (groupDefinitions.isEmpty() || groupDefinitions.size() == 1) {
// No or one prop groups, so we just need a single form for the non-grouped props
// and another one if there is just one group
List<PropertyDefinition> propertyDefinitions = new ArrayList<PropertyDefinition>(
configurationDefinition.getNonGroupedProperties());
if (!propertyDefinitions.isEmpty()) {
DynamicForm form = buildPropertiesForm(propertyDefinitions, configuration);
form.validate();
layout.addMember(form);
}
if (groupDefinitions.size() == 1) {
propertyDefinitions = new ArrayList<PropertyDefinition>(
configurationDefinition.getPropertiesInGroup(groupDefinitions.get(0).getName()));
DynamicForm groupForm = buildPropertiesForm(propertyDefinitions, configuration);
groupForm.setIsGroup(true);
groupForm.setGroupTitle(groupDefinitions.get(0).getDisplayName());
groupForm.validate();
layout.addMember(groupForm);
}
} else {
// Two or more prop groups, so create a section stack with one section per group.
final SectionStack sectionStack = new SectionStack();
sectionStack.setVisibilityMode(VisibilityMode.MULTIPLE);
sectionStack.setWidth100();
sectionStack.setHeight100();
sectionStack.setScrollSectionIntoView(true);
sectionStack.setOverflow(Overflow.AUTO);
if (!configurationDefinition.getNonGroupedProperties().isEmpty()) {
sectionStack.addSection(buildGroupSection(null));
}
for (PropertyGroupDefinition definition : groupDefinitions) {
// com.allen_sauer.gwt.log.client.Log.info("building: " + definition.getDisplayName());
sectionStack.addSection(buildGroupSection(definition));
}
this.toolStrip = buildToolStrip(layout, sectionStack);
layout.addMember(toolStrip);
layout.addMember(sectionStack);
}
fireInitialPropertyChangedEvent();
return layout;
}
private void fireInitialPropertyChangedEvent() {
Map<?, ?> validationErrors = this.topLevelPropertiesValuesManager.getErrors();
if (validationErrors != null) {
for (Object key : validationErrors.keySet()) {
String propertyName = (String) key;
PropertyDefinition propertyDefinition = this.configurationDefinition.get(propertyName);
this.invalidPropertyNameToDisplayNameMap.put(propertyName, propertyDefinition.getDisplayName());
}
}
for (Map.Entry<PropertyDefinitionList, ListGrid> entry : listOfMapsGrids.entrySet()) {
PropertyDefinitionList propertyDefinitionList = entry.getKey();
int listMin = propertyDefinitionList.getMin();
int listMax = propertyDefinitionList.getMax();
ListGridRecord[] gridRecords = entry.getValue().getRecords();
if (!isListGridRecordCountValid(gridRecords, listMin, listMax)) {
this.invalidPropertyNameToDisplayNameMap.put(propertyDefinitionList.getName(),
propertyDefinitionList.getDisplayName());
}
}
Map<?, ?> mapValidationErrors = this.mapLevelPropertiesValuesManager.getErrors();
if (mapValidationErrors != null) {
for (Object key : mapValidationErrors.keySet()) {
String propertyName = (String) key;
Canvas form = this.mapLevelPropertiesValuesManager.getMemberForField(propertyName);
PropertyDefinitionMap mapDefinition = this.formPropertyDefinitionMapMap.get(form.getID());
PropertyDefinition topProperty = getTopLevelPropertyDefinition(mapDefinition);
this.invalidPropertyNameToDisplayNameMap.put(topProperty.getName(), topProperty.getDisplayName());
}
}
if (!this.invalidPropertyNameToDisplayNameMap.isEmpty()) {
PropertyValueChangeEvent event = new PropertyValueChangeEvent(null, null, true,
this.invalidPropertyNameToDisplayNameMap);
firePropertyChangedEvent(event);
}
}
private EnhancedToolStrip buildToolStrip(EnhancedVLayout layout, final SectionStack sectionStack) {
EnhancedToolStrip toolStrip = new EnhancedToolStrip();
toolStrip.setBackgroundImage(null);
toolStrip.setWidth100();
toolStrip.setMembersMargin(3);
toolStrip.setPadding(3);
if (getEditorTitle() != null) {
Label titleLabel = new Label(getEditorTitle());
titleLabel.setWrap(false);
toolStrip.addMember(titleLabel);
}
Menu menu = new Menu();
for (SectionStackSection section : sectionStack.getSections()) {
MenuItem item = new MenuItem(section.getTitle());
item.setAttribute("name", section.getName());
item.addClickHandler(new ClickHandler() {
public void onClick(MenuItemClickEvent event) {
int index = event.getMenu().getItemNum(event.getItem());
sectionStack.expandSection(index);
sectionStack.showSection(index);
}
});
menu.addItem(item);
}
menu.addItem(new MenuItemSeparator());
MenuItem hideAllItem = new MenuItem(MSG.view_configEdit_hideAll());
hideAllItem.addClickHandler(new ClickHandler() {
public void onClick(MenuItemClickEvent event) {
for (int i = 0; i < sectionStack.getSections().length; i++) {
sectionStack.collapseSection(i);
}
}
});
menu.addItem(hideAllItem);
toolStrip.addMember(new IMenuButton(MSG.view_configEdit_jumpToSection(), menu));
return toolStrip;
}
public SectionStackSection buildGroupSection(PropertyGroupDefinition group) {
SectionStackSection section;
if (group == null) {
section = new SectionStackSection(MSG.common_title_generalProp());
section.setExpanded(true);
} else {
String title = "<div style=\"float:left; font-weight: bold;\">"
+ group.getDisplayName()
+ "</div>"
+ (group.getDescription() != null ? ("<div style='border 1px solid red;padding-left: 10px; font-weight: normal; font-size: smaller; float: left;'>"
+ " - " + group.getDescription() + "</div>")
: "");
section = new SectionStackSection(title);
section.setName(group.getName());
section.setExpanded(!group.isDefaultHidden());
}
List<PropertyDefinition> propertyDefinitions = new ArrayList<PropertyDefinition>(
((group == null) ? configurationDefinition.getNonGroupedProperties()
: configurationDefinition.getPropertiesInGroup(group.getName())));
DynamicForm form = buildPropertiesForm(propertyDefinitions, configuration);
// If the group contains any invalid properties, expand the section so the user can see the error icons next to
// the invalid properties.
if (!form.validate()) {
section.setExpanded(true);
}
section.addItem(form);
return section;
}
protected DynamicForm buildPropertiesForm(Collection<PropertyDefinition> propertyDefinitions,
AbstractPropertyMap propertyMap) {
DynamicForm form = new DynamicForm();
if (propertyMap instanceof Configuration) {
this.topLevelPropertiesValuesManager.addMember(form);
}
form.setHiliteRequiredFields(true);
form.setNumCols(4);
form.setCellPadding(5);
form.setColWidths(190, 28, 210);
form.setStyleName("propertiesForm");
List<FormItem> fields = new ArrayList<FormItem>();
addHeaderItems(fields);
addItemsForPropertiesRecursively(propertyDefinitions, propertyMap, fields);
form.setFields(fields.toArray(new FormItem[fields.size()]));
return form;
}
private void addHeaderItems(List<FormItem> fields) {
fields.add(createHeaderTextItem(MSG.view_configEdit_property()));
fields.add(createHeaderTextItem(MSG.view_configEdit_unset()));
fields.add(createHeaderTextItem(MSG.common_title_value()));
fields.add(createHeaderTextItem(MSG.common_title_description()));
}
private StaticTextItem createHeaderTextItem(String value) {
StaticTextItem unsetHeader = new StaticTextItem();
unsetHeader.setValue(value);
unsetHeader.setShowTitle(false);
unsetHeader.setCellStyle("configurationEditorHeaderCell");
return unsetHeader;
}
private void addItemsForPropertiesRecursively(Collection<PropertyDefinition> propertyDefinitions,
AbstractPropertyMap propertyMap, List<FormItem> fields) {
boolean odd = true;
List<PropertyDefinition> sortedPropertyDefinitions = new ArrayList<PropertyDefinition>(propertyDefinitions);
Collections.sort(sortedPropertyDefinitions, new PropertyDefinitionComparator());
for (PropertyDefinition propertyDefinition : sortedPropertyDefinitions) {
Property property = propertyMap.get(propertyDefinition.getName());
if (property == null) {
if (propertyDefinition instanceof PropertyDefinitionSimple) {
property = new PropertySimple(propertyDefinition.getName(), null);
propertyMap.put(property);
}
}
addItemsForPropertyRecursively(propertyDefinition, property, odd, fields);
odd = !odd;
}
return;
}
private void addItemsForPropertyRecursively(PropertyDefinition propertyDefinition, Property property,
boolean oddRow, List<FormItem> fields) {
List<FormItem> fieldsForThisProperty;
if (propertyDefinition instanceof PropertyDefinitionSimple) {
final PropertyDefinitionSimple propertyDefinitionSimple = (PropertyDefinitionSimple) propertyDefinition;
PropertySimple propertySimple = (PropertySimple) property;
fieldsForThisProperty = buildFieldsForPropertySimple(propertyDefinition, propertyDefinitionSimple,
propertySimple);
} else if (propertyDefinition instanceof PropertyDefinitionList) {
///@TODO
PropertyDefinitionList propertyDefinitionList = (PropertyDefinitionList) propertyDefinition;
PropertyDefinition memberDefinition = propertyDefinitionList.getMemberDefinition();
PropertyList propertyList = (PropertyList) property;
if (propertyList == null) {
propertyList = new PropertyList(propertyDefinitionList.getName());
configuration.put(propertyList);
}
fieldsForThisProperty = buildFieldsForPropertyList(propertyDefinition, oddRow, propertyDefinitionList,
memberDefinition, propertyList);
} else if (propertyDefinition instanceof PropertyDefinitionMap) {
PropertyDefinitionMap propertyDefinitionMap = (PropertyDefinitionMap) propertyDefinition;
PropertyMap propertyMap = (PropertyMap) property;
if (propertyMap == null) {
propertyMap = new PropertyMap(propertyDefinitionMap.getName());
configuration.put(propertyMap);
}
fieldsForThisProperty = buildFieldsForPropertyMap(propertyDefinitionMap, propertyMap);
} else {
throw new IllegalStateException("Property definition null or of unknown type: " + propertyDefinition);
}
// Add the fields for this property to the master fields list and set the row background color.
for (FormItem field : fieldsForThisProperty) {
fields.add(field);
field.setCellStyle(oddRow ? "OddRow" : "EvenRow");
}
}
protected List<FormItem> buildFieldsForPropertySimple(PropertyDefinition propertyDefinition,
PropertyDefinitionSimple propertyDefinitionSimple, PropertySimple propertySimple) {
List<FormItem> fields = new ArrayList<FormItem>();
StaticTextItem nameItem = buildNameItem(propertyDefinition);
fields.add(nameItem);
FormItem valueItem;
valueItem = buildSimpleField(propertyDefinitionSimple, propertySimple);
FormItem unsetItem = buildUnsetItem(propertyDefinitionSimple, propertySimple, valueItem);
fields.add(unsetItem);
fields.add(valueItem);
StaticTextItem descriptionItem = buildDescriptionField(propertyDefinition);
fields.add(descriptionItem);
return fields;
}
protected List<FormItem> buildFieldsForPropertyList(PropertyDefinition propertyDefinition, boolean oddRow,
PropertyDefinitionList propertyDefinitionList, PropertyDefinition memberDefinition, PropertyList propertyList) {
List<FormItem> fields = new ArrayList<FormItem>();
StaticTextItem nameItem = buildNameItem(propertyDefinition);
fields.add(nameItem);
if (memberDefinition instanceof PropertyDefinitionMap) {
// List of Maps is a specially supported case with summary fields as columns in a table
// Note: This field spans 3 columns. Since the description column is supplanted, show
// the description as a tooltip when the user hovers over the property name.
nameItem.setTooltip(propertyDefinition.getDescription());
PropertyDefinitionMap memberDefinitionMap = (PropertyDefinitionMap) memberDefinition;
CanvasItem listOfMapsItem = buildListOfMapsField(memberDefinitionMap, propertyDefinitionList, propertyList);
fields.add(listOfMapsItem);
} else if (memberDefinition instanceof PropertyDefinitionSimple) {
SpacerItem spacerItem = new SpacerItem();
fields.add(spacerItem);
CanvasItem listOfSimplesItem = buildListOfSimplesField(propertyDefinitionList, propertyList);
fields.add(listOfSimplesItem);
StaticTextItem descriptionItem = buildDescriptionField(propertyDefinition);
fields.add(descriptionItem);
} else {
Log.error("List " + propertyList + " has unsupported member type: " + memberDefinition);
Canvas canvas = new Canvas();
// TODO: Add label with error message to canvas.
CanvasItem canvasItem = buildComplexPropertyField(canvas);
canvasItem.setColSpan(3);
canvasItem.setEndRow(true);
fields.add(canvasItem);
}
return fields;
}
protected List<FormItem> buildFieldsForPropertyMap(PropertyDefinitionMap propertyDefinitionMap,
PropertyMap propertyMap) {
List<FormItem> fields = new ArrayList<FormItem>();
StaticTextItem nameItem = buildNameItem(propertyDefinitionMap);
fields.add(nameItem);
// Note: This field spans 3 columns.
FormItem mapField = buildMapField(propertyDefinitionMap, propertyMap);
fields.add(mapField);
return fields;
}
protected StaticTextItem buildNameItem(PropertyDefinition propertyDefinition) {
StaticTextItem nameItem = new StaticTextItem();
nameItem.setStartRow(true);
String title = (propertyDefinition.getDisplayName() != null ? propertyDefinition.getDisplayName()
: propertyDefinition.getName());
nameItem.setValue(title);
nameItem.setShowTitle(false);
return nameItem;
}
private StaticTextItem buildDescriptionField(PropertyDefinition propertyDefinition) {
StaticTextItem descriptionItem = new StaticTextItem();
descriptionItem.setValue(propertyDefinition.getDescription());
descriptionItem.setShowTitle(false);
descriptionItem.setEndRow(true);
return descriptionItem;
}
protected void firePropertyChangedEvent(Property property, PropertyDefinition propertyDefinition, boolean isValid) {
PropertyDefinition topLevelPropertyDefinition = getTopLevelPropertyDefinition(propertyDefinition);
boolean invalidPropertySetChanged;
if (isValid) {
invalidPropertySetChanged = (this.invalidPropertyNameToDisplayNameMap.remove(topLevelPropertyDefinition
.getName()) != null);
} else {
invalidPropertySetChanged = (this.invalidPropertyNameToDisplayNameMap.put(
topLevelPropertyDefinition.getName(), topLevelPropertyDefinition.getDisplayName()) == null);
}
PropertyValueChangeEvent event = new PropertyValueChangeEvent(property, propertyDefinition,
invalidPropertySetChanged, this.invalidPropertyNameToDisplayNameMap);
firePropertyChangedEvent(event);
}
private void firePropertyChangedEvent(PropertyValueChangeEvent event) {
for (PropertyValueChangeListener propertyValueChangeListener : this.propertyValueChangeListeners) {
propertyValueChangeListener.propertyValueChanged(event);
}
}
public Map<String, String> getInvalidPropertyNames() {
return this.invalidPropertyNameToDisplayNameMap;
}
private FormItem buildMapField(PropertyDefinitionMap propertyDefinitionMap, final PropertyMap propertyMap) {
boolean isDynamic = isDynamic(propertyDefinitionMap);
if (isDynamic) {
PropertyDefinitionMap propertyDefinitionMapClone = new PropertyDefinitionMap(
propertyDefinitionMap.getName(), propertyDefinitionMap.getDescription(),
propertyDefinitionMap.isRequired());
propertyDefinitionMapClone.setConfigurationDefinition(propertyDefinitionMap.getConfigurationDefinition());
propertyDefinitionMapClone.setReadOnly(propertyDefinitionMap.isReadOnly());
addMemberPropertyDefinitionsToDynamicPropertyMap(propertyDefinitionMapClone, propertyMap);
propertyDefinitionMap = propertyDefinitionMapClone;
}
EnhancedVLayout layout = new EnhancedVLayout();
HTMLFlow description = new HTMLFlow(propertyDefinitionMap.getDescription());
layout.addMember(description);
final PropertyDefinitionMap propertyDefinitionMapFinal = propertyDefinitionMap;
DynamicForm valuesCanvas = buildPropertiesForm(propertyDefinitionMapFinal.getOrderedPropertyDefinitions(),
propertyMap);
layout.addMember(valuesCanvas);
if (propertyDefinitionMap.isRequired()) {
this.mapLevelPropertiesValuesManager.addMember(valuesCanvas);
this.formPropertyDefinitionMapMap.put(valuesCanvas.getID(), propertyDefinitionMap);
valuesCanvas.validate();
}
if (isDynamic && !isReadOnly(propertyDefinitionMap, propertyMap)) {
// Map is not read-only - add footer with New and Delete buttons to allow user to add or remove members.
EnhancedToolStrip buttonBar = new EnhancedToolStrip();
buttonBar.setPadding(5);
buttonBar.setMembersMargin(15);
final IButton newButton = new EnhancedIButton(MSG.common_button_new(), ButtonColor.BLUE);
newButton.setIcon(Window.getImgURL("[SKIN]/actions/add.png"));
newButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
SC.askforValue(MSG.view_configEdit_enterPropName(), new ValueCallback() {
public void execute(String propertyName) {
if (propertyMap.get(propertyName) != null) {
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_configEdit_error_3(propertyName), Message.Severity.Error,
EnumSet.of(Message.Option.Transient)));
} else {
PropertySimple memberPropertySimple = new PropertySimple(propertyName, null);
addPropertyToDynamicMap(memberPropertySimple, propertyMap);
firePropertyChangedEvent(propertyMap, propertyDefinitionMapFinal, true);
reload();
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_configEdit_msg_1(propertyName), EnumSet
.of(Message.Option.Transient)));
}
}
});
}
});
buttonBar.addMember(newButton);
HLayout spacer = new HLayout();
spacer.setWidth(12);
buttonBar.addMember(spacer);
EnhancedHLayout deleteControlsLayout = new EnhancedHLayout();
deleteControlsLayout.setMargin(3);
deleteControlsLayout.setMembersMargin(3);
deleteControlsLayout.setWidth100();
DynamicForm deleteForm = new DynamicForm();
deleteForm.setWidth100();
final SelectItem selectItem = new SortedSelectItem();
selectItem.setMultiple(true);
selectItem.setMultipleAppearance(MultipleAppearance.GRID);
selectItem.setTitle(MSG.common_button_delete());
selectItem.setValueMap(propertyDefinitionMap.getMap().keySet()
.toArray(new String[propertyDefinitionMap.getMap().size()]));
final EnhancedIButton okButton = new EnhancedIButton(MSG.common_button_ok(), ButtonColor.BLUE);
okButton.setDisabled(true);
okButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
SC.confirm(MSG.view_configEdit_confirm_1(), new BooleanCallback() {
@Override
public void execute(Boolean confirmed) {
if (confirmed) {
Object value = selectItem.getValue();
if (value != null) {
String stringValue = value.toString();
String[] memberPropertyNames = stringValue.split(",");
for (final String memberPropertyName : memberPropertyNames) {
PropertySimple memberPropertySimple = propertyMap.getSimple(memberPropertyName);
removePropertyFromDynamicMap(memberPropertySimple);
firePropertyChangedEvent(propertyMap, propertyDefinitionMapFinal, true);
}
}
reload();
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_configEdit_msg_2(), EnumSet.of(Message.Option.Transient)));
}
}
});
}
});
selectItem.addChangedHandler(new ChangedHandler() {
@Override
public void onChanged(ChangedEvent changedEvent) {
Object value = changedEvent.getValue();
if (value != null) {
String stringValue = value.toString();
String[] memberPropertyNames = stringValue.split(",");
okButton.setDisabled(memberPropertyNames.length == 0);
}
}
});
deleteForm.setFields(selectItem);
deleteControlsLayout.addMember(deleteForm);
deleteControlsLayout.addMember(okButton);
buttonBar.addMember(deleteControlsLayout);
layout.addMember(buttonBar);
}
CanvasItem canvasItem = buildComplexPropertyField(layout);
canvasItem.setColSpan(3);
canvasItem.setEndRow(true);
return canvasItem;
}
protected void addPropertyToDynamicMap(PropertySimple memberPropertySimple, PropertyMap propertyMap) {
memberPropertySimple.setOverride(true);
propertyMap.put(memberPropertySimple);
}
protected void removePropertyFromDynamicMap(PropertySimple propertySimple) {
PropertyMap parentMap = propertySimple.getParentMap();
parentMap.getMap().remove(propertySimple.getName());
}
private static boolean isDynamic(PropertyDefinitionMap propertyDefinitionMap) {
Map<String, PropertyDefinition> memberPropertyDefinitions = propertyDefinitionMap.getMap();
return memberPropertyDefinitions.isEmpty();
}
private void addMemberPropertyDefinitionsToDynamicPropertyMap(PropertyDefinitionMap propertyDefinitionMap,
PropertyMap propertyMap) {
for (String propertyName : propertyMap.getMap().keySet()) {
PropertySimple memberPropertySimple = propertyMap.getSimple(propertyName);
if (memberPropertySimple != null) {
PropertyDefinitionSimple memberPropertyDefinitionSimple = new PropertyDefinitionSimple(propertyName,
null, false, PropertySimpleType.STRING);
propertyDefinitionMap.put(memberPropertyDefinitionSimple);
}
}
}
private CanvasItem buildComplexPropertyField(Canvas canvas) {
CanvasItem canvasItem = new CanvasItem();
canvasItem.setCanvas(canvas);
canvasItem.setShowTitle(false);
return canvasItem;
}
private CanvasItem buildListOfMapsField(final PropertyDefinitionMap memberPropertyDefinitionMap,
final PropertyDefinitionList propertyDefinitionList, final PropertyList propertyList) {
Log.debug("Building list-of-maps grid for " + propertyList + "...");
final int listMin = propertyDefinitionList.getMin();
final int listMax = propertyDefinitionList.getMax();
final Canvas errorPanel = buildListOfMapsGridErrorPanel(listMin, listMax);
final ListGrid summaryTable = new ListGrid();
listOfMapsGrids.put(propertyDefinitionList, summaryTable);
summaryTable.setAlternateRecordStyles(true);
summaryTable.setShowAllRecords(true);
summaryTable.setShowAllColumns(true);
summaryTable.setWidth100();
// [BZ 822173 - Table layout problem on configuration page.]
// setBodyOverflow(Overflow.VISIBLE) && setAutoFitFieldWidths(true) issue
//summaryTable.setBodyOverflow(VISIBLE);
//summaryTable.setOverflow(VISIBLE);
// Instead, use setAutoFitData...
summaryTable.setAutoFitData(Autofit.BOTH);
summaryTable.setAutoFitFieldWidths(true);
summaryTable.setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
summaryTable.setRecordEnabledProperty(null);
List<ListGridField> fieldsList = new ArrayList<ListGridField>();
final List<PropertyDefinition> propertyDefinitions = new ArrayList<PropertyDefinition>(
memberPropertyDefinitionMap.getOrderedPropertyDefinitions());
List<PropertyDefinition> summaryPropertyDefinitions = new ArrayList<PropertyDefinition>();
for (PropertyDefinition subDef : propertyDefinitions) {
if (subDef.isSummary()) {
summaryPropertyDefinitions.add(subDef);
}
}
if (summaryPropertyDefinitions.isEmpty()) {
// An extra "feature of the config system". If no fields are labeled summary, all are considered summary.
summaryPropertyDefinitions.addAll(propertyDefinitions);
}
for (PropertyDefinition summaryPropDef : summaryPropertyDefinitions) {
ListGridField field = createListGridField(summaryPropDef);
fieldsList.add(field);
}
boolean allSubDefsReadOnly = isAllReadOnly(propertyDefinitions);
ListGridField editField = new ListGridField("edit", 20);
editField.setType(ListGridFieldType.ICON);
final boolean mapReadOnly = this.readOnly || allSubDefsReadOnly;
String icon = (mapReadOnly) ? ImageManager.getViewIcon() : ImageManager.getEditIcon();
editField.setCellIcon(Window.getImgURL(icon));
editField.setCanEdit(false);
editField.setCanGroupBy(false);
editField.setCanSort(false);
editField.setCanHide(false);
editField.addRecordClickHandler(new RecordClickHandler() {
public void onRecordClick(RecordClickEvent recordClickEvent) {
PropertyMapListGridRecord record = (PropertyMapListGridRecord) recordClickEvent.getRecord();
PropertyMap memberPropertyMap = (PropertyMap) record.getPropertyMap();
Log.debug("Editing property map: " + memberPropertyMap);
displayMapEditor(summaryTable, errorPanel, record, propertyDefinitionList, propertyList,
memberPropertyDefinitionMap, memberPropertyMap, mapReadOnly);
}
});
fieldsList.add(editField);
boolean propertyReadOnly = (readOnly || (!allPropertiesWritable && propertyDefinitionList.isReadOnly()));
if (!propertyReadOnly) {
ListGridField removeField = new ListGridField("remove", 20);
removeField.setType(ListGridFieldType.ICON);
removeField.setCellIcon(Window.getImgURL(ImageManager.getRemoveIcon()));
removeField.setCanEdit(false);
removeField.setCanFilter(true);
removeField.setFilterEditorType(new SpacerItem());
removeField.setCanGroupBy(false);
removeField.setCanSort(false);
removeField.setCanHide(false);
removeField.addRecordClickHandler(new RecordClickHandler() {
public void onRecordClick(final RecordClickEvent recordClickEvent) {
Log.debug("You want to delete: " + recordClickEvent.getRecordNum());
SC.confirm(MSG.view_configEdit_confirm_2(), new BooleanCallback() {
public void execute(Boolean confirmed) {
if (confirmed) {
if (summaryTable.getRecordList().getLength() <= listMin) {
SC.say(MSG.view_configEdit_minBoundsExceeded(String.valueOf(listMin)));
} else {
PropertyMapListGridRecord recordToBeDeleted = (PropertyMapListGridRecord) recordClickEvent
.getRecord();
propertyList.getList().remove(recordToBeDeleted.getIndex());
ListGridRecord[] rows = buildSummaryRecords(propertyList, propertyDefinitions);
boolean listGridRecordCountValid = isListGridRecordCountValid(rows, listMin,
listMax);
if (errorPanel.isVisible() && listGridRecordCountValid) {
errorPanel.setVisible(false);
}
summaryTable.setData(rows);
firePropertyChangedEvent(propertyList, propertyDefinitionList,
listGridRecordCountValid);
}
}
}
});
}
});
editField.setEditorType(new ButtonItem("delete", MSG.common_button_delete()));
fieldsList.add(removeField);
}
summaryTable.setFields(fieldsList.toArray(new ListGridField[fieldsList.size()]));
// Now add rows containing the actual data (i.e. member property values).
ListGridRecord[] rows = buildSummaryRecords(propertyList, propertyDefinitions);
summaryTable.setData(rows);
VLayout summaryTableHolder = new EnhancedVLayout();
ToolStrip toolStrip = new ToolStrip();
toolStrip.setWidth100();
if (!propertyReadOnly) {
IButton addRowButton = new EnhancedIButton();
addRowButton.setWidth("40px");
addRowButton.setIcon(Window.getImgURL(ImageManager.getAddIcon()));
addRowButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
if (propertyList.getList().size() >= listMax) {
SC.say(MSG.view_configEdit_maxBoundsExceeded(String.valueOf(propertyDefinitionList.getMax())));
return;
}
displayMapEditor(summaryTable, errorPanel, null, propertyDefinitionList, propertyList,
memberPropertyDefinitionMap, null, mapReadOnly);
}
});
toolStrip.addMember(addRowButton);
}
if (isListGridRecordCountValid(summaryTable.getRecords(), listMin, listMax)) {
errorPanel.setVisible(false);
} else {
errorPanel.setVisible(true);
}
summaryTableHolder.setMembers(summaryTable, toolStrip, errorPanel);
CanvasItem canvasItem = buildComplexPropertyField(summaryTableHolder);
canvasItem.setColSpan(3);
canvasItem.setEndRow(true);
return canvasItem;
}
private Canvas buildListOfMapsGridErrorPanel(int listMin, int listMax) {
Label errorLabel = buildListOfMapsGridErrorLabelForBounds(listMin, listMax);
final HTMLFlow errorPanel = new HTMLFlow();
errorPanel.setWidth100();
errorPanel.addChild(errorLabel);
return errorPanel;
}
private Label buildListOfMapsGridErrorLabelForBounds(int listMin, int listMax) {
Label errorLabel;
if (listMin == 0 && listMax == Integer.MAX_VALUE) {
errorLabel = new Label("---");
} else if (listMin == 0 && listMax < Integer.MAX_VALUE) {
errorLabel = new Label(MSG.view_configEdit_invalidListSizeMax(String.valueOf(listMax)));
} else if (listMin > 0 && listMax == Integer.MAX_VALUE) {
errorLabel = new Label(MSG.view_configEdit_invalidListSizeMin(String.valueOf(listMin)));
} else {
errorLabel = new Label(MSG.view_configEdit_invalidListSizeMinMax(String.valueOf(listMin),
String.valueOf(listMax)));
}
errorLabel.setWidth100();
errorLabel.setPadding(5);
errorLabel.setHeight(5);
errorLabel.setOverflow(VISIBLE);
errorLabel.addStyleName("InlineError");
errorLabel.setIcon("[SKIN]/actions/exclamation.png");
return errorLabel;
}
private ListGridField createListGridField(PropertyDefinition summaryPropDef) {
ListGridField field = new ListGridField(summaryPropDef.getName(), summaryPropDef.getDisplayName(), 90);
PropertyDefinitionSimple defSimple = (PropertyDefinitionSimple) summaryPropDef;
PropertySimpleType propSimpleType = defSimple.getType();
switch (propSimpleType) {
case BOOLEAN:
field.setType(ListGridFieldType.BOOLEAN);
break;
case INTEGER:
case LONG: // according to what i see in smartgwt forums, INTEGER supports LONG
field.setType(ListGridFieldType.INTEGER);
break;
case FLOAT:
case DOUBLE:
field.setType(ListGridFieldType.FLOAT);
break;
default:
field.setType(ListGridFieldType.TEXT);
}
return field;
}
private static boolean isAllReadOnly(List<PropertyDefinition> propertyDefinitions) {
boolean allPropsDefsReadOnly = true;
for (PropertyDefinition subDef : propertyDefinitions) {
if (!subDef.isReadOnly()) {
Log.debug("Found at least one non-readOnly property for: " + subDef.getName());
allPropsDefsReadOnly = false;
break;
}
}
return allPropsDefsReadOnly;
}
private PropertyMapListGridRecord[] buildSummaryRecords(PropertyList propertyList,
List<PropertyDefinition> definitions) {
PropertyMapListGridRecord[] records = new PropertyMapListGridRecord[propertyList == null ? 0 : propertyList
.getList().size()];
List<Property> list = propertyList.getList();
for (int index = 0, listSize = list.size(); index < listSize; index++) {
Property row = list.get(index);
PropertyMap rowMap = (PropertyMap) row;
PropertyMapListGridRecord record = new PropertyMapListGridRecord(rowMap, index, definitions);
records[index] = record;
}
return records;
}
private CanvasItem buildListOfSimplesField(final PropertyDefinitionList propertyDefinitionList,
final PropertyList propertyList) {
Log.debug("Building list-of-simples field for " + propertyList + "...");
EnhancedVLayout vLayout = new EnhancedVLayout();
final DynamicForm listGrid = new DynamicForm();
vLayout.addMember(listGrid);
final SelectItem membersItem = new SelectItem(propertyList.getName());
membersItem.setShowTitle(false);
membersItem.setMultiple(true);
membersItem.setMultipleAppearance(MultipleAppearance.GRID);
membersItem.setWidth(220);
membersItem.setHeight(60);
LinkedHashMap<String, String> memberValueToIndexMap = buildValueMap(propertyList);
membersItem.setValueMap(memberValueToIndexMap);
listGrid.setItems(membersItem);
if (!isReadOnly(propertyDefinitionList, propertyList)) {
// List is not read-only - add footer with New and Delete buttons to allow user to add or remove members.
ToolStrip footer = new ToolStrip();
footer.setPadding(5);
footer.setWidth100();
footer.setMembersMargin(15);
vLayout.addMember(footer);
final IButton deleteButton = new EnhancedIButton(MSG.common_button_delete());
deleteButton.setIcon(Window.getImgURL(ImageManager.getRemoveIcon()));
deleteButton.setTooltip(MSG.view_configEdit_tooltip_1());
deleteButton.setDisabled(true);
deleteButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
final String[] selectedValues = membersItem.getValues();
final String noun = (selectedValues.length == 1) ? MSG.common_label_item() : MSG
.common_label_items();
String message = MSG.view_configEdit_confirm_3(Integer.toString(selectedValues.length), noun);
if (propertyList.getList().size() <= propertyDefinitionList.getMin()) {
SC.say("You cannot delete this entry because the minimum size bounds has been met: "
+ propertyDefinitionList.getMin());
} else {
SC.ask(message, new BooleanCallback() {
public void execute(Boolean confirmed) {
if (confirmed) {
for (int i = selectedValues.length - 1; i >= 0; i--) {
String selectedValue = selectedValues[i];
int index = Integer.valueOf(selectedValue);
propertyList.getList().remove(index);
// Rebuild the select item options.
LinkedHashMap<String, String> memberValueToIndexMap = buildValueMap(propertyList);
membersItem.setValueMap(memberValueToIndexMap);
deleteButton.disable();
firePropertyChangedEvent(propertyList, propertyDefinitionList, true);
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_configEdit_msg_3(
Integer.toString(selectedValues.length), noun), EnumSet
.of(Message.Option.Transient)));
}
}
}
});
}
}
});
footer.addMember(deleteButton);
membersItem.addChangedHandler(new ChangedHandler() {
@Override
public void onChanged(ChangedEvent changedEvent) {
String[] selectedValues = membersItem.getValues();
int count = selectedValues.length;
deleteButton.setDisabled(count < 1);
}
});
final IButton newButton = new EnhancedIButton(MSG.common_button_new());
newButton.setIcon(Window.getImgURL(ImageManager.getAddIcon()));
newButton.setTooltip(MSG.view_configEdit_tooltip_2());
newButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
if (propertyList.getList().size() >= propertyDefinitionList.getMax()) {
SC.say(MSG.view_configEdit_maxBoundsExceeded(String.valueOf(propertyDefinitionList.getMax())));
} else {
final Window popup = createPopup(MSG.view_configEdit_addItem(), 400, 150);
VLayout vLayout = new VLayout();
vLayout.setMargin(10);
HTMLFlow description = new HTMLFlow(propertyDefinitionList.getDescription());
vLayout.addMember(description);
final DynamicForm form = new DynamicForm();
PropertyDefinitionSimple memberPropertyDefinitionSimple = (PropertyDefinitionSimple) propertyDefinitionList
.getMemberDefinition();
final String propertyName = memberPropertyDefinitionSimple.getName();
final PropertySimple newMemberPropertySimple = new PropertySimple(propertyName, null);
FormItem simpleField = buildSimpleField(memberPropertyDefinitionSimple, newMemberPropertySimple);
simpleField.setTitle(memberPropertyDefinitionSimple.getDisplayName());
simpleField.setShowTitle(true);
simpleField.setAlign(Alignment.CENTER);
simpleField.setDisabled(false);
simpleField.setRequired(true);
simpleField.setEndRow(true);
SpacerItem spacer = new SpacerItem();
spacer.setHeight(9);
form.setItems(simpleField, spacer);
vLayout.addMember(form);
final IButton okButton = new EnhancedIButton(MSG.common_button_ok(), ButtonColor.BLUE);
okButton.disable();
okButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
propertyList.add(newMemberPropertySimple);
// Rebuild the select item options.
LinkedHashMap<String, String> memberValueToIndexMap = buildValueMap(propertyList);
membersItem.setValueMap(memberValueToIndexMap);
firePropertyChangedEvent(propertyList, propertyDefinitionList, true);
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_configEdit_msg_4(), EnumSet.of(Message.Option.Transient)));
popup.destroy();
}
});
form.addItemChangedHandler(new ItemChangedHandler() {
public void onItemChanged(ItemChangedEvent itemChangedEvent) {
Object newValue = itemChangedEvent.getNewValue();
newMemberPropertySimple.setValue(newValue);
// Only enable the OK button, allowing the user to add the property to the map, if the
// property is valid.
boolean isValid = form.validate();
okButton.setDisabled(!isValid);
}
});
final IButton cancelButton = new EnhancedIButton(MSG.common_button_cancel());
cancelButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
popup.destroy();
}
});
ToolStrip buttonBar = new ToolStrip();
buttonBar.setPadding(5);
buttonBar.setWidth100();
buttonBar.setMembersMargin(15);
buttonBar.setAlign(Alignment.CENTER);
buttonBar.setMembers(okButton, cancelButton);
vLayout.addMember(buttonBar);
popup.addItem(vLayout);
popup.show();
simpleField.focusInItem();
}
}
});
footer.addMember(newButton);
}
return buildComplexPropertyField(vLayout);
}
private static LinkedHashMap<String, String> buildValueMap(PropertyList propertyList) {
LinkedHashMap<String, String> memberValueToIndexMap = new LinkedHashMap<String, String>();
List<Property> memberProperties = propertyList.getList();
int index = 0;
for (Iterator<Property> iterator = memberProperties.iterator(); iterator.hasNext();) {
Property memberProperty = iterator.next();
PropertySimple memberPropertySimple = (PropertySimple) memberProperty;
String memberValue = memberPropertySimple.getStringValue();
if (memberValue == null) {
Log.error("List " + propertyList + " contains property with null value - removing and skipping...");
iterator.remove();
continue;
}
memberValueToIndexMap.put(String.valueOf(index++), memberValue);
}
return memberValueToIndexMap;
}
protected FormItem buildSimpleField(final PropertyDefinitionSimple propertyDefinitionSimple,
final PropertySimple propertySimple) {
String value = propertySimple.getStringValue();
if (null == value && null != propertyDefinitionSimple.getDefaultValue()) {
value = propertyDefinitionSimple.getDefaultValue();
}
FormItem valueItem = null;
boolean propertyIsReadOnly = isReadOnly(propertyDefinitionSimple, propertySimple);
Log.debug("Building simple field for " + propertySimple + "(read-only:" + propertyIsReadOnly + ")...");
// TODO (ips, 03/25/11): We eventually want to use StaticTextItems for read-only PASSWORD props too, but we have
// to wait until we implement masking/unmasking of PASSWORD props at the SLSB layer first.
if (propertyIsReadOnly && propertyDefinitionSimple.getType() != PropertySimpleType.PASSWORD) {
valueItem = new StaticTextItem();
String escapedValue = StringUtility.escapeHtml(value);
valueItem.setValue(preserveTextFormatting ? "<pre>" + escapedValue + "</pre>" : escapedValue);
} else {
List<PropertyDefinitionEnumeration> enumeratedValues = propertyDefinitionSimple.getEnumeratedValues();
if (enumeratedValues != null && !enumeratedValues.isEmpty()) {
LinkedHashMap<String, String> valueOptions = new LinkedHashMap<String, String>();
for (PropertyDefinitionEnumeration option : propertyDefinitionSimple.getEnumeratedValues()) {
valueOptions.put(option.getValue(), option.getName());
}
if (propertyDefinitionSimple.getAllowCustomEnumeratedValue()) {
valueItem = new ComboBoxItem();
((ComboBoxItem) valueItem).setAddUnknownValues(true);
} else {
if (valueOptions.size() > 3) {
valueItem = new SelectItem();
} else {
valueItem = new RadioGroupItem();
}
}
valueItem.setValueMap(valueOptions);
} else {
switch (propertyDefinitionSimple.getType()) {
case STRING:
case FILE:
case DIRECTORY:
case LONG:
// Treat values with type LONG as strings, since GWT does not support longs.
valueItem = new TextItem();
break;
case LONG_STRING:
valueItem = new TextAreaItem();
break;
case PASSWORD:
valueItem = new PasswordItem();
valueItem.setAttribute("autocomplete", "off");
break;
case BOOLEAN:
RadioGroupItem radioGroupItem = new RadioGroupItem();
radioGroupItem.setVertical(false);
radioGroupItem.setValueMap(BOOLEAN_PROPERTY_ITEM_VALUE_MAP);
valueItem = radioGroupItem;
break;
case INTEGER:
SpinnerItem spinnerItem = new SpinnerItem();
spinnerItem.setMin(Integer.MIN_VALUE);
spinnerItem.setMax(Integer.MAX_VALUE);
valueItem = spinnerItem;
break;
case FLOAT:
case DOUBLE:
valueItem = new FloatItem();
break;
}
}
valueItem.setDisabled(propertyIsReadOnly || isUnset(propertyDefinitionSimple, propertySimple));
List<Validator> validators = buildValidators(propertyDefinitionSimple, propertySimple);
valueItem.setValidators(validators.toArray(new Validator[validators.size()]));
if (isUnset(propertyDefinitionSimple, propertySimple)) {
setValue(valueItem, null);
} else {
valueItem.setValue(preserveTextFormatting ? "<pre>" + value + "</pre>" : value);
}
if ((propertySimple.getConfiguration() != null) || (propertySimple.getParentMap() != null)
|| (propertySimple.getParentList() != null)) {
valueItem.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent changedEvent) {
updatePropertySimpleValue(changedEvent.getItem(), changedEvent.getValue(), propertySimple,
propertyDefinitionSimple);
// Only fire a prop value change event if the prop's a top-level simple or a simple within a
// top-level map.
if (shouldFireEventOnPropertyValueChange(changedEvent.getItem(), propertyDefinitionSimple,
propertySimple)) {
boolean isValid = changedEvent.getItem().validate();
firePropertyChangedEvent(propertySimple, propertyDefinitionSimple, isValid);
}
}
});
// Since spinnerItems only fire ChangedEvent once the spinner buttons are pushed
// we add blur handler to pick up any changes to that field when leaving
if (valueItem instanceof SpinnerItem) {
valueItem.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
updatePropertySimpleValue(event.getItem(), event.getItem().getValue(), propertySimple,
propertyDefinitionSimple);
// Only fire a prop value change event if the prop's a top-level simple or a simple within a
// top-level map.
if (shouldFireEventOnPropertyValueChange(event.getItem(), propertyDefinitionSimple,
propertySimple)) {
boolean isValid = event.getItem().validate();
firePropertyChangedEvent(propertySimple, propertyDefinitionSimple, isValid);
}
}
});
}
}
}
valueItem.setName(propertySimple.getName());
valueItem.setTitle("none");
valueItem.setShowTitle(false);
setValueAsTooltipIfAppropriate(valueItem, preserveTextFormatting ? "<pre>" + value + "</pre>" : value);
valueItem.setRequired(propertyDefinitionSimple.isRequired());
valueItem.setWidth(220);
/*
TODO: Click handlers seem to be turned off for disabled fields... need to find an alternative.
valueItem.addClickHandler(new com.smartgwt.client.widgets.form.fields.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
com.allen_sauer.gwt.log.client.Log.info("Click in value field");
clickEvent.getItem().setDisabled(false);
unsetItem.setValue(false);
}
});
*/
return valueItem;
}
private void setValueAsTooltipIfAppropriate(FormItem formItem, String value) {
if (((formItem instanceof TextItem) && !(formItem instanceof PasswordItem))
|| (formItem instanceof TextAreaItem)) {
formItem.setTooltip(value);
}
}
protected boolean shouldFireEventOnPropertyValueChange(FormItem formItem,
PropertyDefinitionSimple propertyDefinitionSimple, PropertySimple propertySimple) {
PropertyMap parentMap = propertySimple.getParentMap();
return propertySimple.getConfiguration() != null || (parentMap != null && parentMap.getConfiguration() != null);
}
protected void updatePropertySimpleValue(FormItem formItem, Object value, PropertySimple propertySimple,
PropertyDefinitionSimple propertyDefinitionSimple) {
propertySimple.setErrorMessage(null);
propertySimple.setValue(value);
String stringValue = (value != null) ? value.toString() : null;
setValueAsTooltipIfAppropriate(formItem, stringValue);
}
protected static PropertyDefinition getTopLevelPropertyDefinition(PropertyDefinition propertyDefinition) {
PropertyDefinition currentPropertyDefinition = propertyDefinition;
while (currentPropertyDefinition.getConfigurationDefinition() == null) {
if (currentPropertyDefinition.getParentPropertyListDefinition() != null) {
currentPropertyDefinition = currentPropertyDefinition.getParentPropertyListDefinition();
} else if (currentPropertyDefinition.getParentPropertyMapDefinition() != null) {
currentPropertyDefinition = currentPropertyDefinition.getParentPropertyMapDefinition();
} else {
Log.error("Property definition " + currentPropertyDefinition + " has no parent.");
break;
}
}
return currentPropertyDefinition;
}
protected static Property getTopLevelProperty(Property property) {
Property currentProperty = property;
while (currentProperty.getConfiguration() == null) {
if (currentProperty.getParentList() != null) {
currentProperty = currentProperty.getParentList();
} else if (currentProperty.getParentMap() != null) {
currentProperty = currentProperty.getParentMap();
} else {
Log.error("Property " + currentProperty + " has no parent.");
break;
}
}
return currentProperty;
}
protected FormItem buildUnsetItem(final PropertyDefinitionSimple propertyDefinitionSimple,
final PropertySimple propertySimple, final FormItem valueItem) {
FormItem item;
if (!propertyDefinitionSimple.isRequired()) {
final CheckboxItem unsetItem = new CheckboxItem("unset-" + valueItem.getName());
boolean unset = isUnset(propertyDefinitionSimple, propertySimple);
unsetItem.setValue(unset);
unsetItem.setDisabled(isReadOnly(propertyDefinitionSimple, propertySimple));
unsetItem.setShowLabel(false);
unsetItem.setShowTitle(false);
unsetItem.setLabelAsTitle(false);
unsetItem.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent changeEvent) {
// treat the ChangedEvent as a possible focus change, since this is a checkbox
handleFocusChange(valueItem, unsetItem);
Boolean isUnset = (Boolean) changeEvent.getValue();
if (isUnset != valueItem.isDisabled()) {
valueItem.setDisabled(isUnset);
}
if (isUnset) {
if (valueItem.getValue() != null) {
setValue(valueItem, null);
updatePropertySimpleValue(unsetItem, null, propertySimple, propertyDefinitionSimple);
firePropertyChangedEvent(propertySimple, propertyDefinitionSimple, true);
}
} else {
String defaultValue = propertyDefinitionSimple.getDefaultValue();
if (defaultValue != null) {
setValue(valueItem, defaultValue);
updatePropertySimpleValue(unsetItem, defaultValue, propertySimple, propertyDefinitionSimple);
boolean isValid = valueItem.validate();
firePropertyChangedEvent(propertySimple, propertyDefinitionSimple, isValid);
}
valueItem.focusInItem();
}
valueItem.redraw();
}
});
valueItem.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent changedEvent) {
// If new value is null, select the unset checkbox; otherwise, deselect it.
Object value = changedEvent.getValue();
boolean isUnset = (value == null);
unsetItem.setValue(isUnset);
}
});
// (Smartgwt 3.0) Certain FormItems, like SelectItem, generate a Blur event when we don't want them to.
// For example, expanding the SelectItem drop down (via click) generates a Blur event (which makes no
// sense to me since the user is obviously not leaving the widget, it should still be in focus imo). Anyway,
// to get around this behavior and to be able to unset config properties that a user has left as null values
// when changing focus, we avoid BlurHandler and instead use FocusHandlers and a DIY blur handling
// approach.
valueItem.addFocusHandler(new FocusHandler() {
public void onFocus(FocusEvent event) {
handleFocusChange(valueItem, unsetItem);
}
});
item = unsetItem;
} else {
item = new SpacerItem();
item.setShowTitle(false);
}
return item;
}
private void handleFocusChange(FormItem valueItem, CheckboxItem unsetItem) {
if (!(null == blurValueItem || valueItem.equals(blurValueItem))) {
boolean isUnset = (null == blurValueItem.getValue());
if (isUnset != blurValueItem.isDisabled()) {
blurValueItem.setDisabled(isUnset);
blurValueItem.redraw();
}
if (isUnset != blurUnsetItem.getValueAsBoolean()) {
blurUnsetItem.setValue(isUnset);
blurUnsetItem.redraw();
}
}
blurValueItem = valueItem;
blurUnsetItem = unsetItem;
}
private boolean isUnset(PropertyDefinitionSimple propertyDefinition, PropertySimple propertySimple) {
return (!propertyDefinition.isRequired() && (propertySimple == null || propertySimple.getStringValue() == null));
}
protected boolean isReadOnly(PropertyDefinition propertyDefinition, Property property) {
if (this.readOnly) {
return true;
}
if (this.allPropertiesWritable) {
return false;
}
String errorMessage = property.getErrorMessage();
if ((errorMessage != null) && (!errorMessage.trim().equals(""))) {
// special case 1: property has a plugin-set error message - allow user to edit it even if it's a read-only prop,
// otherwise the user will have no way to give the property a new value and thereby get things to a valid state
return false;
}
if (property instanceof PropertySimple) {
PropertySimple propertySimple = (PropertySimple) property;
if (propertyDefinition.isRequired() && (propertySimple.getStringValue() == null)
|| "".equals(propertySimple.getStringValue())) {
// special case 2: required simple prop with no value - allow user to edit it even if it's a read-only prop,
// otherwise the user will have no way to give the property a new value and thereby get things to a valid state
return false;
}
}
return (propertyDefinition.isReadOnly());
}
protected List<Validator> buildValidators(PropertyDefinitionSimple propertyDefinition, PropertySimple property) {
List<Validator> validators = new ArrayList<Validator>();
Validator typeValidator = null;
switch (propertyDefinition.getType()) {
case STRING:
case LONG_STRING:
case FILE:
case DIRECTORY:
LengthRangeValidator lengthRangeValidator = new LengthRangeValidator();
lengthRangeValidator.setMax(PropertySimple.MAX_VALUE_LENGTH);
typeValidator = lengthRangeValidator;
break;
case INTEGER:
typeValidator = new IsIntegerValidator();
break;
case LONG:
typeValidator = new IsLongValidator();
break;
case FLOAT:
case DOUBLE:
typeValidator = new IsFloatValidator();
break;
default:
break;
}
if (typeValidator != null) {
validators.add(typeValidator);
}
Set<Constraint> constraints = propertyDefinition.getConstraints();
if (constraints != null) {
for (Constraint constraint : constraints) {
if (constraint instanceof IntegerRangeConstraint) {
IntegerRangeConstraint integerConstraint = ((IntegerRangeConstraint) constraint);
IntegerRangeValidator validator = new IntegerRangeValidator();
if (integerConstraint.getMinimum() != null) {
validator.setMin(integerConstraint.getMinimum().intValue());
} else {
validator.setMin(Integer.MIN_VALUE);
}
if (integerConstraint.getMaximum() != null) {
validator.setMax(integerConstraint.getMaximum().intValue());
} else {
validator.setMax(Integer.MAX_VALUE);
}
validators.add(validator);
} else if (constraint instanceof FloatRangeConstraint) {
FloatRangeConstraint floatConstraint = ((FloatRangeConstraint) constraint);
FloatRangeValidator validator = new FloatRangeValidator();
if (floatConstraint.getMinimum() != null) {
validator.setMin(floatConstraint.getMinimum().floatValue());
}
if (floatConstraint.getMaximum() != null) {
validator.setMax(floatConstraint.getMaximum().floatValue());
}
validators.add(validator);
} else if (constraint instanceof RegexConstraint) {
RegExpValidator validator = new RegExpValidator("^" + constraint.getDetails() + "$");
validators.add(validator);
}
}
}
PluginReportedErrorValidator validator = new PluginReportedErrorValidator(property);
validators.add(validator);
return validators;
}
private void displayMapEditor(final ListGrid summaryTable, final Canvas errorPanel,
final PropertyMapListGridRecord existingRecord, final PropertyDefinitionList propertyDefinitionList,
final PropertyList propertyList, PropertyDefinitionMap memberMapDefinition, final PropertyMap memberMap,
final boolean mapReadOnly) {
final List<PropertyDefinition> memberDefinitions = new ArrayList<PropertyDefinition>(
memberMapDefinition.getOrderedPropertyDefinitions());
final boolean newRow = (memberMap == null);
final PropertyMap workingMap = (newRow) ? new PropertyMap(memberMapDefinition.getName()) : memberMap
.deepCopy(true);
final String title = (mapReadOnly) ? MSG.view_configEdit_viewRow() : MSG.view_configEdit_editRow();
final Window popup = createPopup(title, 800, 600);
final EnhancedVLayout layout = new EnhancedVLayout();
layout.setHeight100();
layout.setMargin(8);
final DynamicForm childForm = buildPropertiesForm(memberDefinitions, workingMap);
childForm.setHeight100();
layout.addMember(childForm);
ToolStrip buttonBar = new ToolStrip();
buttonBar.setPadding(5);
buttonBar.setWidth100();
buttonBar.setMembersMargin(15);
buttonBar.setAlign(Alignment.CENTER);
final IButton okButton = new EnhancedIButton(MSG.common_button_ok(), ButtonColor.BLUE);
if (!mapReadOnly) {
okButton.disable();
}
okButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
if (!mapReadOnly) {
if (!childForm.validate()) {
okButton.disable();
return;
}
if (newRow) {
propertyList.add(workingMap);
int index = propertyList.getList().size() - 1;
PropertyMapListGridRecord record = new PropertyMapListGridRecord(workingMap, index,
memberDefinitions);
summaryTable.addData(record);
} else {
mergePropertyMap(workingMap, memberMap, memberDefinitions);
existingRecord.refresh();
summaryTable.updateData(existingRecord);
}
boolean listGridRecordCountValid = isListGridRecordCountValid(summaryTable.getRecords(),
propertyDefinitionList.getMin(), propertyDefinitionList.getMax());
if (errorPanel.isVisible() && listGridRecordCountValid) {
errorPanel.setVisible(false);
}
firePropertyChangedEvent(propertyList, propertyDefinitionList, listGridRecordCountValid);
summaryTable.redraw();
}
//reset the custom blur event handling - we're destroying the new form here
blurUnsetItem = null;
blurValueItem = null;
layout.destroy();
popup.destroy();
}
});
// Only enable the OK button if all properties are valid.
childForm.addItemChangedHandler(new ItemChangedHandler() {
public void onItemChanged(ItemChangedEvent itemChangedEvent) {
// TODO (ips, 03/14/11): Ideally, we would validate the form here, but it's preventing boolean prop
// radio buttons from being selectable, most likely due to a SmartGWT bug.
//okButton.setDisabled(!childForm.validate());
okButton.setDisabled(false);
}
});
buttonBar.addMember(okButton);
if (!mapReadOnly) {
final IButton cancelButton = new IButton(MSG.common_button_cancel());
cancelButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
public void onClick(ClickEvent clickEvent) {
layout.destroy();
popup.destroy();
}
});
buttonBar.addMember(cancelButton);
}
//reset the custom blur event handling - we're creating a new form here
blurUnsetItem = null;
blurValueItem = null;
layout.addMember(buttonBar);
popup.addItem(layout);
popup.show();
}
private Window createPopup(String title, int width, int height) {
final Window popup = new Window();
popup.setTitle(title);
popup.setWidth(width);
popup.setHeight(height);
popup.setIsModal(true);
popup.setShowModalMask(true);
popup.centerInPage();
return popup;
}
private void mergePropertyMap(PropertyMap sourceMap, PropertyMap targetMap,
List<PropertyDefinition> memberDefinitions) {
for (PropertyDefinition subDef : memberDefinitions) {
PropertyDefinitionSimple subDefSimple = (PropertyDefinitionSimple) subDef;
PropertySimple propertySimple = ((PropertySimple) sourceMap.get(subDefSimple.getName()));
String newValue = (propertySimple != null) ? propertySimple.getStringValue() : null;
PropertySimple existingProp = targetMap.getSimple(subDefSimple.getName());
if (null == existingProp) {
if (null != newValue) {
existingProp = new PropertySimple(subDefSimple.getName(), newValue);
targetMap.put(existingProp);
}
} else {
existingProp.setStringValue(newValue);
}
}
}
protected static void setValue(FormItem item, Object value) {
if (value instanceof String) {
item.setValue((String) value);
} else if (value instanceof Boolean) {
item.setValue(value);
} else if (value instanceof Integer) {
item.setValue(value);
} else if (value instanceof Float) {
item.setValue(value);
} else if (value instanceof Double) {
item.setValue(value);
} else if (value instanceof Date) {
item.setValue((Date) value);
} else {
String stringValue = (value != null) ? value.toString() : null;
item.setValue(stringValue);
}
item.setDefaultValue((String) null);
}
private static class PropertyDefinitionComparator implements Comparator<PropertyDefinition> {
public int compare(PropertyDefinition o1, PropertyDefinition o2) {
return new Integer(o1.getOrder()).compareTo(o2.getOrder());
}
}
private class PluginReportedErrorValidator extends CustomValidator {
private Property property;
public PluginReportedErrorValidator(Property property) {
this.property = property;
}
@Override
protected boolean condition(Object value) {
String errorMessage = this.property.getErrorMessage();
boolean valid = (errorMessage == null);
if (!valid) {
setErrorMessage(errorMessage);
}
return valid;
}
}
}