/*
* RHQ Management Platform
* Copyright (C) 2005-2008 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, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser 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.core.gui.configuration;
import java.util.Collection;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIParameter;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.component.html.HtmlPanelGrid;
import javax.faces.component.html.HtmlPanelGroup;
import javax.faces.component.html.HtmlSelectBooleanCheckbox;
import javax.faces.context.FacesContext;
import org.ajax4jsf.component.html.HtmlAjaxCommandLink;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.richfaces.component.html.HtmlModalPanel;
import org.rhq.core.domain.configuration.AbstractPropertyMap;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyDefinitionDynamic;
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.PropertyDefinition;
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.gui.RequestParameterNameConstants;
import org.rhq.core.gui.configuration.helper.PropertyRenderingUtility;
import org.rhq.core.gui.configuration.propset.ConfigurationSetComponent;
import org.rhq.core.gui.util.FacesComponentUtility;
import org.rhq.core.gui.util.FacesExpressionUtility;
import org.rhq.core.gui.util.PropertyIdGeneratorUtility;
import org.rhq.core.template.TemplateEngine;
/**
* A factory that generates a tree of JSF components that depicts a given collection of JON {@link Property}s.
*
* @author Ian Springer
*/
public abstract class AbstractPropertyBagUIComponentTreeFactory {
private final Log LOG = LogFactory.getLog(AbstractPropertyBagUIComponentTreeFactory.class);
static final String DELETE_LIST_MEMBER_PROPERTY_FUNCTION = "DELETE_LIST_MEMBER_PROPERTY";
static final String DELETE_OPEN_MAP_MEMBER_PROPERTY_FUNCTION = "DELETE_OPEN_MAP_MEMBER_PROPERTY";
static final String PARAM_ID_SUFFIX = "-param";
static final String PANEL_ID_SUFFIX = "-panel";
protected static final String PROPERTY_SIMPLE_OVERRIDE_ACCESSOR_SUFFIX = "override";
protected static final String PROPERTY_SIMPLE_VALUE_ACCESSOR_SUFFIX = "stringValue";
protected static final String PROPERTY_LIST_VALUE_ACCESSOR_SUFFIX = "list";
protected static final String PROPERTY_MAP_VALUE_ACCESSOR_SUFFIX = "map";
private static final String VIEW_MAP_BUTTON_TITLE = "View Details";
private static final String VIEW_MAP_BUTTON_LABEL = "View";
private static final String EDIT_MAP_BUTTON_TITLE = "Edit";
private static final String EDIT_MAP_BUTTON_LABEL = "Edit";
private static final String DELETE_MAP_BUTTON_LABEL = "Delete";
private static final String DELETE_MAP_BUTTON_TITLE = "Delete";
private static final String ADD_NEW_MAP_BUTTON_LABEL = "Add New";
private static final String ADD_NEW_MAP_BUTTON_TITLE = "Add New";
private static final String NESTED_PROPERTIES_TABLE_STYLE_CLASS = "nested-properties-table";
private static final String PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS = "properties-table-header-cell";
private static final String NESTED_PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS = "nested-properties-table-header-cell";
private static final String NESTED_PROPERTIES_TABLE_INDENT_CELL_STYLE_CLASS = "nested-properties-table-indent-cell";
private static final String PROPERTY_ERROR_CELL_STYLE_CLASS = "property-error-cell";
private static final String OPENMAP_PROPERTIES_TABLE_STYLE_CLASS = "openmap-properties-table";
private static final String OPENMAP_PROPERTY_DISPLAY_NAME_CELL_STYLE_CLASS = "openmap-property-display-name-cell";
private static final String OPENMAP_PROPERTY_VALUE_CELL_STYLE_CLASS = "openmap-property-value-cell";
private static final String OPENMAP_PROPERTY_ACTIONS_CELL_STYLE_CLASS = "openmap-property-actions-cell";
private static final String OPENMAP_PROPERTY_ERROR_CELL_STYLE_CLASS = "openmap-property-error-cell";
private static final String LIST_PROPERTY_DISPLAY_NAME_CELL_STYLE_CLASS = "list-property-display-name-cell";
private static final String LIST_PROPERTY_VALUE_CELL_STYLE_CLASS = "list-property-value-cell";
private static final String LIST_PROPERTY_DESCRIPTION_CELL_STYLE_CLASS = "list-property-description-cell";
private static final String LIST_PROPERTY_ENABLED_CELL_STYLE_CLASS = "list-property-enabled-cell";
private static final String LIST_PROPERTY_CHILDREN_CELL_STYLE_CLASS = "list-property-children-cell";
private static final String PROPERTY_MAP_SUMMARY_TABLE_STYLE_CLASS = "property-map-summary-table";
private static final String PROPERTY_MAP_SUMMARY_DATA_HEADER_CELL_STYLE_CLASS = "property-map-summary-data-header-cell";
private static final String PROPERTY_MAP_SUMMARY_HEADER_TEXT_STYLE_CLASS = "property-map-summary-header-text";
private static final String PROPERTY_MAP_SUMMARY_BUTTONS_CELL_STYLE_CLASS = "property-map-summary-buttons-cell";
private static final String PROPERTY_MAP_SUMMARY_DATA_CELL_STYLE_CLASS = "property-map-summary-data-cell";
private static final String PROPERTY_MAP_SUMMARY_DATA_TEXT_STYLE_CLASS = "property-map-summary-data-text";
private static final String PROPERTY_MAP_SUMMARY_BUTTON_FOOTER_STYLE_CLASS = "property-buttonfooterrow";
private static final String BUTTONS_TABLE_STYLE_CLASS = "buttons-table";
private static final String VALUES_DIFFER_TEXT_STYLE_CLASS = "values-differ-text";
private AbstractConfigurationComponent config;
private Collection<PropertyDefinition> propertyDefinitions;
private AbstractPropertyMap propertyMap;
private boolean topLevel;
private String valueExpressionFormat;
private String overrideExpressionFormat;
private boolean isGroup;
private HtmlModalPanel memberValuesModalPanel;
public AbstractPropertyBagUIComponentTreeFactory(AbstractConfigurationComponent config,
Collection<PropertyDefinition> propertyDefinitions, AbstractPropertyMap propertyMap, boolean topLevel,
String valueExpressionFormat) {
this.config = config;
this.propertyDefinitions = propertyDefinitions;
this.propertyMap = propertyMap;
this.topLevel = topLevel;
this.valueExpressionFormat = valueExpressionFormat;
this.overrideExpressionFormat = getOverrideExpressionFormat(valueExpressionFormat);
// The below variable is for the new group config impl being implemented by Ian.
this.isGroup = (this.config instanceof ConfigurationSetComponent);
if (this.isGroup) {
ConfigurationSetComponent configurationSetComponent = (ConfigurationSetComponent) this.config;
this.memberValuesModalPanel = configurationSetComponent.getPropSetModalPanel();
}
}
private String getOverrideExpressionFormat(String valueExpression) {
// replace '.stringValue}' suffix with '.override}'
int index = valueExpression.lastIndexOf(".");
return valueExpression.substring(0, index) + "." + PROPERTY_SIMPLE_OVERRIDE_ACCESSOR_SUFFIX + "}";
}
protected Integer getListIndex() {
return null;
}
public UIComponent createUIComponentTree(String rowStyleClass) {
HtmlPanelGroup rootPanel = FacesComponentUtility.createBlockPanel(this.config,
FacesComponentUtility.NO_STYLE_CLASS);
if (!this.propertyDefinitions.isEmpty() || (this.propertyMap instanceof PropertyMap)) {
if (!this.propertyDefinitions.isEmpty()) {
String tableStyleClass = this.topLevel ? CssStyleClasses.PROPERTIES_TABLE
: NESTED_PROPERTIES_TABLE_STYLE_CLASS;
FacesComponentUtility.addVerbatimText(rootPanel, "\n\n<table class='" + tableStyleClass + "'>");
int rowCount = 0;
boolean alternateRowStyles = (rowStyleClass == null);
if (containsSimpleProperties()) {
addSimplePropertiesTableHeaders(rootPanel); // the Name/Unset/Value/Description headers
}
// TODO: Display simple props before lists and maps, so the simple props immediately follow the
// Name/Unset/Value/Description header.
for (PropertyDefinition propertyDefinition : this.propertyDefinitions) {
rowStyleClass = getRowStyleClass(rowStyleClass, rowCount, alternateRowStyles);
addProperty(rootPanel, propertyDefinition, rowStyleClass);
rowCount++;
}
} else {
// If the map contains no member definitions, it means the map permits arbitrary simple properties as
// members. We refer to such a map as an "open map."
PropertyDefinitionMap propertyDefinitionMap = this.config.getConfigurationDefinition()
.getPropertyDefinitionMap(((PropertyMap) this.propertyMap).getName());
FacesComponentUtility.addVerbatimText(rootPanel, "\n\n<table class='"
+ OPENMAP_PROPERTIES_TABLE_STYLE_CLASS + "'>");
addOpenMapMemberPropertiesTableHeaders(rootPanel, propertyDefinitionMap);
for (Property property : this.propertyMap.getMap().values()) {
if (!(property instanceof PropertySimple)) {
throw new IllegalStateException("Open map " + this.propertyMap
+ " contains non-simple property: " + property);
}
PropertySimple propertySimple = (PropertySimple) property;
// NOTE: Row colors never alternate for maps.
addOpenMapMemberProperty(rootPanel, propertyDefinitionMap, propertySimple, rowStyleClass);
}
addNewOpenMapMemberPropertyRow(rootPanel, propertyDefinitionMap);
}
FacesComponentUtility.addVerbatimText(rootPanel, "</table>");
}
return rootPanel;
}
private String getRowStyleClass(String rowStyleClass, int rowCount, boolean alternateRowStyles) {
if (alternateRowStyles) {
// add the odd/even row style
rowStyleClass = ((rowCount % 2) == 0) ? CssStyleClasses.ROW_ODD : CssStyleClasses.ROW_EVEN;
}
return rowStyleClass;
}
private void addSimplePropertiesTableHeaders(UIComponent parent) {
String headerCellStyleClass = this.topLevel ? PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS
: NESTED_PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS;
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr>");
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Name", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
// TODO: Get rid of the Override column, once the new group config stuff is operational.
if (this.config.isGroup()) {
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Override", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
}
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Unset", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Value", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Description", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
FacesComponentUtility.addVerbatimText(parent, "</tr>");
}
private boolean containsSimpleProperties() {
boolean containsSimples = false;
for (PropertyDefinition propertyDefinition : this.propertyDefinitions) {
if (propertyDefinition instanceof PropertyDefinitionSimple) {
containsSimples = true;
break;
}
}
return containsSimples;
}
private void addOpenMapMemberPropertiesTableHeaders(UIComponent parent, PropertyDefinitionMap propertyDefinitionMap) {
String headerCellStyleClass = this.topLevel ? PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS
: NESTED_PROPERTIES_TABLE_HEADER_CELL_STYLE_CLASS;
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr>");
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Name", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Value", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
if (!isReadOnly(propertyDefinitionMap)) {
FacesComponentUtility.addVerbatimText(parent, "<th class='" + headerCellStyleClass + "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Actions", FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>");
}
FacesComponentUtility.addVerbatimText(parent, "</tr>");
}
protected AbstractConfigurationComponent getConfigurationComponent() {
return config;
}
private void addProperty(UIComponent parent, PropertyDefinition propertyDefinition, String rowStyleClass) {
if (propertyDefinition instanceof PropertyDefinitionSimple) {
addSimpleProperty(parent, (PropertyDefinitionSimple) propertyDefinition, rowStyleClass, config.getTemplateEngine());
} else if (propertyDefinition instanceof PropertyDefinitionList) {
addListProperty(parent, (PropertyDefinitionList) propertyDefinition, rowStyleClass);
} else if (propertyDefinition instanceof PropertyDefinitionMap) {
addMapProperty(parent, (PropertyDefinitionMap) propertyDefinition, rowStyleClass);
} else if (propertyDefinition instanceof PropertyDefinitionDynamic) {
addDynamicProperty(parent, (PropertyDefinitionDynamic) propertyDefinition, rowStyleClass);
}
else {
throw new IllegalStateException("Unsupported subclass of " + PropertyDefinition.class.getName() + ".");
}
}
/**
* Adds a new property to the UI based on a dynamic property definition.
*
* @param parent owner in which the UI rendering of the property will live
* @param propertyDefinitionDynamic describes the property being rendered
* @param rowStyleClass indicates the CSS style (i.e. background color) of this row in the property list
*/
private void addDynamicProperty(UIComponent parent, PropertyDefinitionDynamic propertyDefinitionDynamic,
String rowStyleClass) {
addDebug(parent, true, ".addDynamicProperty()");
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>");
PropertySimple propertySimple = this.propertyMap.getSimple(propertyDefinitionDynamic.getName());
ValueExpression propertyValueExpression = createPropertyValueExpression(propertySimple.getName(),
this.valueExpressionFormat);
UIInput input = null;
if (!this.isGroup || (propertySimple.getOverride() != null && propertySimple.getOverride())) {
// We need to create the input component ahead of when we need to add it to the component tree, since we
// need to know the input component's id in order to render the unset control.
input = PropertyRenderingUtility.createInputForDynamicProperty(propertyDefinitionDynamic, propertySimple,
propertyValueExpression, getListIndex(), this.isGroup, this.config.isReadOnly(), this.config
.isFullyEditable(), this.config.isPrevalidate());
}
FacesComponentUtility
.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_DISPLAY_NAME_CELL + "'>");
PropertyRenderingUtility.addPropertyDisplayName(parent, propertyDefinitionDynamic, propertySimple, this.config
.isReadOnly());
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_ENABLED_CELL + "'>");
if (!propertyDefinitionDynamic.isRequired())
PropertyRenderingUtility.addUnsetControl(parent, propertyDefinitionDynamic.isRequired(),
propertyDefinitionDynamic.isReadOnly(), propertySimple, this.config.getListIndex(), input, this.isGroup,
this.config.isReadOnly(), this.config.isFullyEditable());
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_VALUE_CELL + "'>");
addPropertySimpleValue(parent, input, propertyValueExpression);
FacesComponentUtility.addVerbatimText(parent, "<br/>");
if (input != null)
PropertyRenderingUtility.addMessageComponentForInput(parent, input);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_DESCRIPTION_CELL + "'>");
PropertyRenderingUtility.addPropertyDescription(parent, propertyDefinitionDynamic);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "</tr>");
addDebug(parent, false, ".addSimpleProperty()");
}
private void addSimpleProperty(UIComponent parent, PropertyDefinitionSimple propertyDefinitionSimple,
String rowStyleClass, TemplateEngine templateEngine) {
addDebug(parent, true, ".addSimpleProperty()");
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>");
PropertySimple propertySimple = this.propertyMap.getSimple(propertyDefinitionSimple.getName());
ValueExpression propertyValueExpression = createPropertyValueExpression(propertySimple.getName(),
this.valueExpressionFormat);
UIInput input = null;
if (!this.isGroup || (propertySimple.getOverride() != null && propertySimple.getOverride()))
// We need to create the input component ahead of when we need to add it to the component tree, since we
// need to know the input component's id in order to render the unset control.
input = PropertyRenderingUtility.createInputForSimpleProperty(propertyDefinitionSimple, propertySimple,
propertyValueExpression, getListIndex(), this.isGroup, this.config.isReadOnly(), this.config
.isFullyEditable(), this.config.isPrevalidate(), templateEngine);
FacesComponentUtility
.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_DISPLAY_NAME_CELL + "'>");
PropertyRenderingUtility.addPropertyDisplayName(parent, propertyDefinitionSimple, propertySimple, this.config
.isReadOnly());
FacesComponentUtility.addVerbatimText(parent, "</td>");
// TODO: Get rid of the override checkbox once the group plugin config has been converted over to the new
// group config GUI.
if (this.config.isGroup()) {
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_ENABLED_CELL + "'>");
addPropertyOverrideControl(parent, propertyDefinitionSimple, input);
FacesComponentUtility.addVerbatimText(parent, "</td>");
}
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_ENABLED_CELL + "'>");
if (!propertyDefinitionSimple.isRequired())
PropertyRenderingUtility.addUnsetControl(parent, propertyDefinitionSimple.isRequired(),
propertyDefinitionSimple.isReadOnly(), propertySimple, this.config.getListIndex(), input, this.isGroup,
this.config.isReadOnly(), this.config.isFullyEditable());
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_VALUE_CELL + "'>");
addPropertySimpleValue(parent, input, propertyValueExpression);
FacesComponentUtility.addVerbatimText(parent, "<br/>");
if (input != null)
PropertyRenderingUtility.addMessageComponentForInput(parent, input);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + CssStyleClasses.PROPERTY_DESCRIPTION_CELL + "'>");
PropertyRenderingUtility.addPropertyDescription(parent, propertyDefinitionSimple);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "</tr>");
addDebug(parent, false, ".addSimpleProperty()");
}
private void addPropertySimpleValue(UIComponent parent, @Nullable UIInput input,
ValueExpression propertyValueExpression) {
if (!this.isGroup) {
parent.getChildren().add(input);
return;
}
HtmlPanelGrid panelGrid = FacesComponentUtility.addPanelGrid(parent, null, 2, CssStyleClasses.BUTTONS_TABLE);
panelGrid.setColumnClasses("group-property-value-cell, group-property-members-icon-cell");
if (input != null)
panelGrid.getChildren().add(input);
else
FacesComponentUtility
.addOutputText(panelGrid, null, "Member Values Differ", VALUES_DIFFER_TEXT_STYLE_CLASS);
// Add the Members button which will display the "drill-down" modal panel.
HtmlAjaxCommandLink ajaxCommandLink = FacesComponentUtility.createComponent(HtmlAjaxCommandLink.class);
panelGrid.getChildren().add(ajaxCommandLink);
ajaxCommandLink.setOncomplete("Richfaces.showModalPanel('"
+ this.memberValuesModalPanel.getClientId(FacesContext.getCurrentInstance()) + "');");
//ajaxCommandLink.setReRender("rhq_propSet");
String verb = this.config.isReadOnly() ? "View" : "Edit";
String tooltip = verb + " Member Values";
ajaxCommandLink.setTitle(tooltip);
FacesComponentUtility.addParameter(ajaxCommandLink, null, "propertyExpressionString", propertyValueExpression
.getExpressionString());
FacesComponentUtility.addParameter(ajaxCommandLink, null, "refresh", ConfigRenderer.PROPERTY_SET_COMPONENT_ID);
FacesComponentUtility.addGraphicImage(ajaxCommandLink, null, "/images/icn_member_configs.png", null);
}
private void addOpenMapMemberProperty(HtmlPanelGroup parent, PropertyDefinitionMap propertyDefinitionMap,
PropertySimple propertySimple, String rowStyleClass) {
addDebug(parent, true, ".addOpenMapMemberProperty()");
String mapName = ((PropertyMap) this.propertyMap).getName();
String memberName = propertySimple.getName();
NullComponent wrapper = new NullComponent();
parent.getChildren().add(wrapper);
String wrapperId = PropertyIdGeneratorUtility.getIdentifier(propertySimple, getListIndex(), PANEL_ID_SUFFIX);
wrapper.setId(wrapperId);
FacesComponentUtility.addVerbatimText(wrapper, "\n\n<tr class='" + rowStyleClass + "'>");
FacesComponentUtility.addVerbatimText(wrapper, "<td class='" + OPENMAP_PROPERTY_DISPLAY_NAME_CELL_STYLE_CLASS
+ "'>");
FacesComponentUtility.addOutputText(wrapper, this.config, propertySimple.getName(),
CssStyleClasses.PROPERTY_DISPLAY_NAME_TEXT);
FacesComponentUtility.addVerbatimText(wrapper, "</td>");
FacesComponentUtility.addVerbatimText(wrapper, "<td class='" + OPENMAP_PROPERTY_VALUE_CELL_STYLE_CLASS + "'>");
String expressionString = String.format(valueExpressionFormat, propertySimple.getName());
ValueExpression valueExpression = FacesExpressionUtility.createValueExpression(expressionString, String.class);
UIInput input = null;
if (!this.isGroup || propertySimple.getOverride() != null && propertySimple.getOverride())
input = PropertyRenderingUtility.createInputForSimpleProperty(propertySimple, valueExpression, this.config
.isReadOnly());
addPropertySimpleValue(wrapper, input, valueExpression);
FacesComponentUtility.addVerbatimText(wrapper, "</td>");
if (!isReadOnly(propertyDefinitionMap)) {
// add Actions column w/ delete button
FacesComponentUtility.addVerbatimText(wrapper, "<td class='" + OPENMAP_PROPERTY_ACTIONS_CELL_STYLE_CLASS
+ "'>");
HtmlCommandLink deleteLink = FacesComponentUtility.createComponent(HtmlCommandLink.class);
wrapper.getChildren().add(deleteLink);
deleteLink.setTitle(DELETE_MAP_BUTTON_TITLE);
deleteLink.setImmediate(true); // skip validation (we only want to validate upon Save)
// See http://jira.rhq-project.org/browse/RHQ-1792 for more info on why the below line is commented out.
//deleteLink.setOnclick("return prepareInputsForSubmission(this.form);");
FacesComponentUtility.addParameter(deleteLink, this.config, RequestParameterNameConstants.FUNCTION_PARAM,
DELETE_OPEN_MAP_MEMBER_PROPERTY_FUNCTION);
FacesComponentUtility.addParameter(deleteLink, this.config, RequestParameterNameConstants.MAP_NAME_PARAM,
mapName);
FacesComponentUtility.addParameter(deleteLink, this.config,
RequestParameterNameConstants.MEMBER_NAME_PARAM, memberName);
FacesComponentUtility.addButton(deleteLink, DELETE_MAP_BUTTON_LABEL, CssStyleClasses.BUTTON_SMALL);
FacesComponentUtility.addVerbatimText(wrapper, "</td>");
}
FacesComponentUtility.addVerbatimText(wrapper, "</tr>");
addDebug(wrapper, false, ".addOpenMapMemberProperty()");
}
/**
* Adds a single row property
*/
private void addListProperty(UIComponent parent, PropertyDefinitionList listPropertyDefinition, String rowStyleClass) {
addDebug(parent, true, ".addListProperty()");
if (!this.topLevel) {
throw new IllegalStateException("Lists are only supported at the top level of a Configuration.");
}
PropertyList listProperty = this.propertyMap.getList(listPropertyDefinition.getName());
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_DISPLAY_NAME_CELL_STYLE_CLASS
+ "'>");
PropertyRenderingUtility.addPropertyDisplayName(parent, listPropertyDefinition, listProperty, this.config
.isReadOnly());
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_ENABLED_CELL_STYLE_CLASS + "' />");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_VALUE_CELL_STYLE_CLASS + "' />");
FacesComponentUtility
.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_DESCRIPTION_CELL_STYLE_CLASS + "'>");
PropertyRenderingUtility.addPropertyDescription(parent, listPropertyDefinition);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "</tr>");
PropertyDefinition listMemberPropertyDefinition = listPropertyDefinition.getMemberDefinition();
if (listMemberPropertyDefinition instanceof PropertyDefinitionMap) {
if (!this.isGroup) {
addListMemberMapProperties(parent, listProperty, (PropertyDefinitionMap) listMemberPropertyDefinition,
rowStyleClass);
} else {
addErrorRow(
parent,
"Viewing/editing of lists of"
+ " properties is not yet supported for group Configurations."
+ " To view/edit this property for group members, please go to the Configure>Current tab for each member separately.",
rowStyleClass);
}
} else {
addErrorRow(parent, "Viewing/editing of lists of "
+ listMemberPropertyDefinition.getClass().getSimpleName()
+ " properties is not yet supported. Here's the list's toString() value for now: "
+ ((listProperty != null) ? listProperty.getList() : null), rowStyleClass);
}
addDebug(parent, false, ".addListProperty()");
}
private void addListMemberMapProperties(UIComponent parent, PropertyList listProperty,
PropertyDefinitionMap listMemberPropertyDefinition, String rowStyleClass) {
addDebug(parent, true, ".addListMemberMapProperties()");
PropertyDefinitionMap listMemberMapPropertyDefinition = listMemberPropertyDefinition;
validateMapDefinition(listMemberMapPropertyDefinition);
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>\n");
FacesComponentUtility.addVerbatimText(parent, "<td colspan='4' class='"
+ LIST_PROPERTY_CHILDREN_CELL_STYLE_CLASS + "'>");
Collection<PropertyDefinition> mapSummaryPropertyDefinitions = listMemberMapPropertyDefinition
.getSummaryPropertyDefinitions();
// Start the "List of maps" table
FacesComponentUtility.addVerbatimText(parent, "\n<!-- start list of maps -->\n");
FacesComponentUtility.addVerbatimText(parent, "<table class='" + PROPERTY_MAP_SUMMARY_TABLE_STYLE_CLASS
+ "'>\n");
FacesComponentUtility.addVerbatimText(parent, "<tbody>\n");
// Add the row for the headers
FacesComponentUtility.addVerbatimText(parent, "<tr>\n");
// Add column header cells...
for (PropertyDefinition summaryPropertyDefinition : listMemberMapPropertyDefinition
.getSummaryPropertyDefinitions()) {
// We can safely cast here, because we already validated the map member definitions.
PropertyDefinitionSimple mapMemberSimplePropertyDefinition = (PropertyDefinitionSimple) listMemberMapPropertyDefinition
.get(summaryPropertyDefinition.getName());
FacesComponentUtility.addVerbatimText(parent, "<th class='"
+ PROPERTY_MAP_SUMMARY_DATA_HEADER_CELL_STYLE_CLASS + "'>");
FacesComponentUtility.addOutputText(parent, this.config,
mapMemberSimplePropertyDefinition.getDisplayName(), PROPERTY_MAP_SUMMARY_HEADER_TEXT_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>\n");
}
// add the "Actions" header
FacesComponentUtility.addVerbatimText(parent, "<th class='" + PROPERTY_MAP_SUMMARY_DATA_HEADER_CELL_STYLE_CLASS
+ "'>");
FacesComponentUtility.addOutputText(parent, this.config, "Actions",
PROPERTY_MAP_SUMMARY_HEADER_TEXT_STYLE_CLASS);
FacesComponentUtility.addVerbatimText(parent, "</th>\n");
FacesComponentUtility.addVerbatimText(parent, "</tr>\n");
// Add body cells...
// NOTE: We only display the View button if the config component is read-only. If the list is read-only, members
// cannot be added or deleted, but existing members can still be edited.
String viewEditButtonLabel = (this.config.isReadOnly()) ? VIEW_MAP_BUTTON_LABEL : EDIT_MAP_BUTTON_LABEL;
String viewEditButtonTitle = (this.config.isReadOnly()) ? VIEW_MAP_BUTTON_TITLE : EDIT_MAP_BUTTON_TITLE;
for (int index = 0; index < listProperty.getList().size(); index++) {
addListMemberMapProperty(parent, listProperty, listMemberMapPropertyDefinition, viewEditButtonLabel,
viewEditButtonTitle, index);
}
//HtmlPanelGrid buttonsPanelGrid = FacesComponentUtility.addPanelGrid(parent, this.configurationUIBean, 1, BUTTONS_TABLE_STYLE_CLASS);
if (!isReadOnly(listMemberMapPropertyDefinition.getParentPropertyListDefinition())) {
FacesComponentUtility.addVerbatimText(parent, "<tr>\n");
FacesComponentUtility.addVerbatimText(parent, "<td colspan='" + (mapSummaryPropertyDefinitions.size() + 1)
+ "' class='" + PROPERTY_MAP_SUMMARY_BUTTON_FOOTER_STYLE_CLASS + "'>");
// add-new button
HtmlCommandLink addNewLink = FacesComponentUtility.addCommandLink(parent, this.config);
addNewLink.setTitle(ADD_NEW_MAP_BUTTON_TITLE);
MethodExpression actionExpression = FacesExpressionUtility.createMethodExpression(
"#{ConfigHelperUIBean.addNewMap}", String.class, new Class[0]);
addNewLink.setActionExpression(actionExpression);
FacesComponentUtility.addParameter(addNewLink, this.config, RequestParameterNameConstants.LIST_NAME_PARAM,
listProperty.getName());
FacesComponentUtility.addParameter(addNewLink, this.config, RequestParameterNameConstants.LIST_INDEX_PARAM,
String.valueOf(listProperty.getList().size()));
FacesComponentUtility.addButton(addNewLink, ADD_NEW_MAP_BUTTON_LABEL, CssStyleClasses.BUTTON_SMALL);
FacesComponentUtility.addVerbatimText(parent, "</td></tr>\n");
}
FacesComponentUtility.addVerbatimText(parent, "</tbody></table>\n\n");
FacesComponentUtility.addVerbatimText(parent, "<!-- end List of Maps -->\n\n");
FacesComponentUtility.addVerbatimText(parent, "</td></tr>");
addDebug(parent, false, ".addListMemberMapProperties()");
}
private void addListMemberMapProperty(UIComponent parent, PropertyList listProperty,
PropertyDefinitionMap listMemberMapPropertyDefinition, String viewEditButtonLabel, String viewEditButtonTitle,
int index) {
addDebug(parent, true, ".addListMemberMapProperty()");
Property listMemberProperty = listProperty.getList().get(index);
String listName = listProperty.getName();
if (!(listMemberProperty instanceof PropertyMap)) {
throw new IllegalStateException("Property '" + listName
+ "' is defined as a list of maps but contains one or more non-map members.");
}
PropertyMap listMemberPropertyMap = (PropertyMap) listMemberProperty;
NullComponent wrapper = new NullComponent();
parent.getChildren().add(wrapper);
String wrapperId = PropertyIdGeneratorUtility.getIdentifier(listMemberProperty, index, PANEL_ID_SUFFIX);
wrapper.setId(wrapperId);
FacesComponentUtility.addVerbatimText(wrapper, "<tr>\n");
// add the simple property data cells
addPropertyMapSummaryDataCells(wrapper, listMemberMapPropertyDefinition, listMemberPropertyMap);
int numberOfButtons = 1;
if (!this.config.isReadOnly()) {
numberOfButtons++;
}
// add the Actions cell
FacesComponentUtility.addVerbatimText(wrapper, "<td class='" + PROPERTY_MAP_SUMMARY_BUTTONS_CELL_STYLE_CLASS
+ "'>");
HtmlPanelGrid buttonsPanelGrid = FacesComponentUtility.addPanelGrid(wrapper, this.config, numberOfButtons,
BUTTONS_TABLE_STYLE_CLASS);
// view/edit button
HtmlCommandLink viewEditLink = FacesComponentUtility.addCommandLink(buttonsPanelGrid, this.config);
viewEditLink.setTitle(viewEditButtonTitle);
MethodExpression actionExpression = FacesExpressionUtility.createMethodExpression(
"#{ConfigHelperUIBean.accessMap}", String.class, new Class[0]);
viewEditLink.setActionExpression(actionExpression);
FacesComponentUtility.addParameter(viewEditLink, this.config, RequestParameterNameConstants.LIST_NAME_PARAM,
listName);
String listIndex = String.valueOf(index);
FacesComponentUtility.addParameter(viewEditLink, this.config, RequestParameterNameConstants.LIST_INDEX_PARAM,
listIndex);
int configId = this.config.getConfiguration().getId();
FacesComponentUtility.addParameter(viewEditLink, this.config, RequestParameterNameConstants.CONFIG_ID_PARAM,
String.valueOf(configId));
FacesComponentUtility.addButton(viewEditLink, viewEditButtonLabel, CssStyleClasses.BUTTON_SMALL);
if (!isReadOnly(listMemberMapPropertyDefinition.getParentPropertyListDefinition())) {
// delete button
HtmlCommandLink deleteLink = FacesComponentUtility.addCommandLink(buttonsPanelGrid, this.config);
deleteLink.setTitle(DELETE_MAP_BUTTON_TITLE);
//deleteLink.setImmediate(true);
FacesComponentUtility.addParameter(deleteLink, this.config, RequestParameterNameConstants.FUNCTION_PARAM,
DELETE_LIST_MEMBER_PROPERTY_FUNCTION);
FacesComponentUtility.addParameter(deleteLink, this.config, RequestParameterNameConstants.LIST_NAME_PARAM,
listName);
UIParameter listIndexParam = FacesComponentUtility.addParameter(deleteLink, this.config,
RequestParameterNameConstants.LIST_INDEX_PARAM, listIndex);
String paramId = PropertyIdGeneratorUtility.getIdentifier(listMemberProperty, index, PARAM_ID_SUFFIX);
listIndexParam.setId(paramId);
FacesComponentUtility.addButton(deleteLink, DELETE_MAP_BUTTON_LABEL, CssStyleClasses.BUTTON_SMALL);
}
FacesComponentUtility.addVerbatimText(wrapper, "</td>\n");
FacesComponentUtility.addVerbatimText(wrapper, "</tr>\n");
addDebug(parent, false, ".addListMemberMapProperty()");
}
private void addPropertyMapSummaryDataCells(UIComponent parent,
PropertyDefinitionMap listMemberMapPropertyDefinition, PropertyMap listMemberMapProperty) {
for (PropertyDefinition summaryPropertyDefinition : listMemberMapPropertyDefinition
.getSummaryPropertyDefinitions()) {
Property mapMemberProperty = listMemberMapProperty.get(summaryPropertyDefinition.getName());
if (!(mapMemberProperty instanceof PropertySimple)) {
throw new IllegalStateException("Property '" + mapMemberProperty.getName()
+ "' is defined as a map of simples but contains one or more non-simple members.");
}
PropertySimple mapMemberSimpleProperty = (PropertySimple) mapMemberProperty;
FacesComponentUtility.addVerbatimText(parent, "<td class='" + PROPERTY_MAP_SUMMARY_DATA_CELL_STYLE_CLASS
+ "'>");
if (mapMemberSimpleProperty.getStringValue() == null) {
FacesComponentUtility.addOutputText(parent, this.config, "not set",
CssStyleClasses.REQUIRED_MARKER_TEXT);
} else {
FacesComponentUtility.addOutputText(parent, this.config, mapMemberSimpleProperty.getStringValue(),
PROPERTY_MAP_SUMMARY_DATA_TEXT_STYLE_CLASS);
}
FacesComponentUtility.addVerbatimText(parent, "</td>\n");
}
}
private void validateMapDefinition(PropertyDefinitionMap propertyDefinitionMap) {
for (PropertyDefinition mapMemberPropertyDefinition : propertyDefinitionMap.getMap().values()) {
if (!(mapMemberPropertyDefinition instanceof PropertyDefinitionSimple)) {
throw new IllegalStateException("Only maps of simple properties are supported.");
}
}
}
private void addMapProperty(UIComponent parent, PropertyDefinitionMap propertyDefinitionMap, String rowStyleClass) {
if (!this.topLevel) {
throw new IllegalStateException(
"Maps are only supported at the top level of a Configuration or within a top-level List.");
}
validateMapDefinition(propertyDefinitionMap);
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_DISPLAY_NAME_CELL_STYLE_CLASS
+ "'>");
PropertyRenderingUtility.addPropertyDisplayName(parent, propertyDefinitionMap, this.propertyMap
.get(propertyDefinitionMap.getName()), this.config.isReadOnly());
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_ENABLED_CELL_STYLE_CLASS + "' />");
FacesComponentUtility.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_VALUE_CELL_STYLE_CLASS + "' />");
FacesComponentUtility
.addVerbatimText(parent, "<td class='" + LIST_PROPERTY_DESCRIPTION_CELL_STYLE_CLASS + "'>");
PropertyRenderingUtility.addPropertyDescription(parent, propertyDefinitionMap);
FacesComponentUtility.addVerbatimText(parent, "</td>");
FacesComponentUtility.addVerbatimText(parent, "</tr>");
addMapMemberProperties(parent, propertyDefinitionMap.getName(), rowStyleClass);
}
private void addMapMemberProperties(UIComponent parent, String mapName, String rowStyleClass) {
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'>\n");
FacesComponentUtility.addVerbatimText(parent, "<td colspan='4' class='"
+ LIST_PROPERTY_CHILDREN_CELL_STYLE_CLASS + "'>");
HtmlPanelGrid spacerPanelGrid = FacesComponentUtility.addPanelGrid(parent, this.config, 2,
FacesComponentUtility.NO_STYLE_CLASS);
spacerPanelGrid.setWidth("100%");
spacerPanelGrid.setColumnClasses(NESTED_PROPERTIES_TABLE_INDENT_CELL_STYLE_CLASS + ","
+ FacesComponentUtility.NO_STYLE_CLASS);
FacesComponentUtility.addBlockPanel(spacerPanelGrid, this.config, FacesComponentUtility.NO_STYLE_CLASS);
AbstractPropertyBagUIComponentTreeFactory propertyListUIComponentTreeFactory = new MapInConfigurationUIComponentTreeFactory(
this.config, mapName);
spacerPanelGrid.getChildren().add(
propertyListUIComponentTreeFactory.createUIComponentTree(CssStyleClasses.ROW_EVEN));
parent.getChildren().add(spacerPanelGrid);
FacesComponentUtility.addVerbatimText(parent, "</td></tr>\n");
}
private void addNewOpenMapMemberPropertyRow(UIComponent parent, PropertyDefinitionMap propertyDefinitionMap) {
if (!isReadOnly(propertyDefinitionMap)) {
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr><td colspan='3' class='"
+ PROPERTY_MAP_SUMMARY_BUTTON_FOOTER_STYLE_CLASS + "'>");
// add-new button
HtmlCommandLink addNewLink = FacesComponentUtility.addCommandLink(parent, this.config);
addNewLink.setTitle(ADD_NEW_MAP_BUTTON_TITLE);
//addNewLink.setImmediate(true); // skip validation (we only want to validate upon Save)
MethodExpression actionExpression = FacesExpressionUtility.createMethodExpression(
"#{ConfigHelperUIBean.addNewOpenMapMemberProperty}", String.class, new Class[0]);
addNewLink.setActionExpression(actionExpression);
FacesComponentUtility.addParameter(addNewLink, this.config, RequestParameterNameConstants.MAP_NAME_PARAM,
propertyDefinitionMap.getName());
FacesComponentUtility.addButton(addNewLink, ADD_NEW_MAP_BUTTON_LABEL, CssStyleClasses.BUTTON_SMALL);
FacesComponentUtility.addVerbatimText(parent, "</td></tr>");
}
}
private void addErrorRow(UIComponent parent, String errorMsg, String rowStyleClass) {
addDebug(parent, true, ".addErrorRow()");
FacesComponentUtility.addVerbatimText(parent, "\n\n<tr class='" + rowStyleClass + "'><td colspan='4' class='"
+ PROPERTY_ERROR_CELL_STYLE_CLASS + "'><h4>" + errorMsg + "</h4></td></tr>");
addDebug(parent, false, ".addErrorRow()");
}
@NotNull
private HtmlSelectBooleanCheckbox createInputOverrideForSimpleProperty(
PropertyDefinitionSimple propertyDefinitionSimple) {
HtmlSelectBooleanCheckbox input = FacesComponentUtility.createComponent(HtmlSelectBooleanCheckbox.class,
this.config);
// Find the actual property corresponding to this property def, and use that to create the component id.
Property property = this.propertyMap.get(propertyDefinitionSimple.getName());
if (property != null) {
// add suffix to prevent collision with value input identifier for property
String propertyId = PropertyIdGeneratorUtility.getIdentifier(property, getListIndex(), "override");
input.setId(propertyId);
Boolean shouldOverride = ((PropertySimple) property).getOverride();
FacesComponentUtility.setOverride(input, (shouldOverride != null && shouldOverride));
}
setInputOverrideExpression(input, propertyDefinitionSimple.getName());
return input;
}
private void addPropertyOverrideControl(UIComponent parent, PropertyDefinitionSimple propertyDefinitionSimple,
UIInput valueInput) {
HtmlSelectBooleanCheckbox overrideCheckbox = createInputOverrideForSimpleProperty(propertyDefinitionSimple);
parent.getChildren().add(overrideCheckbox);
overrideCheckbox.setValue(isOverride(propertyDefinitionSimple));
if (isReadOnly(propertyDefinitionSimple)) {
FacesComponentUtility.setDisabled(overrideCheckbox, true);
}
/* TODO: use unsetCheckbox and valueInput to implement the following logic against dom elements in javascript:
*
* if override input is not checked, disable unsetCheckbox and dissble valueInput dom elements * if override
* input is checked , enabled unsetCheckbox ** only cascade enable valueInput dom elements if the
* unsetCheckbox is NOT unset
*/
StringBuilder onclick = new StringBuilder();
for (String valueInputHtmlDomReference : PropertyRenderingUtility.getHtmlDomReferences(valueInput)) {
onclick.append("setInputOverride(").append(valueInputHtmlDomReference).append(", this.checked);");
}
overrideCheckbox.setOnclick(onclick.toString());
}
/**
* Binds the value of the specified UIInput to an EL expression corresponding to the Configuration property's
* override attribute with the specified name.
*/
@SuppressWarnings("deprecation")
private void setInputOverrideExpression(HtmlSelectBooleanCheckbox input, String propertyName) {
// e.g.: #{configuration.simpleProperties['useJavaContext'].override}
String expression = String.format(this.overrideExpressionFormat, propertyName);
ValueExpression valueExpression = FacesExpressionUtility.createValueExpression(expression, Boolean.class);
input.setValueExpression("value", valueExpression);
}
private Boolean isOverride(PropertyDefinition propertyDefinition) {
if (!(propertyDefinition instanceof PropertyDefinitionSimple)) {
return false;
}
Property property = this.propertyMap.get(propertyDefinition.getName());
if (property == null) {
return false;
}
Boolean override = ((PropertySimple) property).getOverride();
return ((override != null) && override);
}
private boolean isReadOnly(PropertyDefinition propertyDefinition) {
// A fully editable config overrides any other means of setting read only.
return (!this.config.isFullyEditable() && (this.config.isReadOnly() || (propertyDefinition.isReadOnly() && !isInvalidRequiredProperty(propertyDefinition))));
}
private boolean isInvalidRequiredProperty(PropertyDefinition propertyDefinition) {
boolean isInvalidRequiredProperty = false;
if ((propertyDefinition instanceof PropertyDefinitionSimple) && propertyDefinition.isRequired()) {
PropertySimple propertySimple = this.propertyMap.getSimple(propertyDefinition.getName());
String errorMessage = propertySimple.getErrorMessage();
if ((null == propertySimple.getStringValue()) || "".equals(propertySimple.getStringValue())
|| ((null != errorMessage) && (!"".equals(errorMessage.trim())))) {
// Required properties with no value, or an invalid value (assumed if we see an error message) should never
// be set to read-only, otherwise the user will have no way to give the property a value and thereby
// get things to a valid state.
isInvalidRequiredProperty = true;
}
}
return isInvalidRequiredProperty;
}
/**
* simple method that will add debugging HTML comments so that I can figure out which part of the HTML is generated
* by which part of this class
*
* @param component the component this comment should be added
* @param start true if this is the "START" comment, false if it is the "END" comment
* @param methodName the name of the method this is calling from
*/
private void addDebug(UIComponent component, boolean start, String methodName) {
if (LOG.isDebugEnabled()) {
StringBuilder msg = new StringBuilder("\n<!--");
msg.append(start ? " START " : " END ");
msg.append(this.getClass().getSimpleName());
msg.append(methodName);
msg.append(" -->\n ");
FacesComponentUtility.addVerbatimText(component, msg);
}
}
/**
* Binds the value of the specified UIInput to an EL expression corresponding to the Configuration property with the
* specified name.
*/
@SuppressWarnings( { "JavaDoc" })
private static ValueExpression createPropertyValueExpression(String propertyName, String valueExpressionFormat) {
// e.g.: #{configuration.simpleProperties['useJavaContext'].stringValue}
String expression = String.format(valueExpressionFormat, propertyName);
ValueExpression valueExpression = FacesExpressionUtility.createValueExpression(expression, String.class);
return valueExpression;
}
}