/*
* (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library 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
* Lesser General Public License for more details.
*
* Contributors:
* Anahide Tchertchian
*/
package org.nuxeo.ecm.platform.forms.layout.facelets.plugins;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.component.html.HtmlSelectManyCheckbox;
import javax.faces.component.html.HtmlSelectManyListbox;
import javax.faces.component.html.HtmlSelectManyMenu;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.CompositeFaceletHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.FaceletHandler;
import javax.faces.view.facelets.TagAttributes;
import javax.faces.view.facelets.TagConfig;
import org.apache.commons.lang.ArrayUtils;
import org.nuxeo.ecm.platform.forms.layout.api.BuiltinWidgetModes;
import org.nuxeo.ecm.platform.forms.layout.api.Widget;
import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOption;
import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOptions;
import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException;
import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetSelectOptionImpl;
import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetSelectOptionsImpl;
import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper;
import org.nuxeo.ecm.platform.forms.layout.facelets.LeafFaceletHandler;
import org.nuxeo.ecm.platform.ui.web.component.UISelectItem;
import org.nuxeo.ecm.platform.ui.web.component.UISelectItems;
import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils;
/**
* Helper class for options generation depending on the widget definition
*
* @since 5.4.2
*/
public abstract class AbstractSelectWidgetTypeHandler extends
AbstractWidgetTypeHandler {
private static final long serialVersionUID = 1L;
protected enum SelectPropertyMappings {
selectOptions, var, itemLabel, resolveItemLabelTwice, itemLabelPrefix, itemLabelPrefixSeparator,
//
itemLabelSuffix, itemLabelSuffixSeparator, itemValue,
//
itemRendered, itemDisabled, itemEscaped, ordering, caseSensitive,
//
displayIdAndLabel, displayIdAndLabelSeparator, notDisplayDefaultOption,
//
localize, dbl10n;
}
// ease up override of behavior without impacting default options
// management
protected Map<String, Serializable> getOptionProperties(FaceletContext ctx,
Widget widget, WidgetSelectOption selectOption) {
Map<String, Serializable> props = new HashMap<>();
for (SelectPropertyMappings mapping : SelectPropertyMappings.values()) {
if (widget.getProperties().containsKey(mapping.name())) {
props.put(mapping.name(), widget.getProperty(mapping.name()));
}
}
return props;
}
protected String getOptionComponentType(WidgetSelectOption selectOption) {
if (selectOption instanceof WidgetSelectOptions) {
return UISelectItems.COMPONENT_TYPE;
} else {
return UISelectItem.COMPONENT_TYPE;
}
}
protected FaceletHandler getOptionFaceletHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
WidgetSelectOption selectOption, FaceletHandler nextHandler) {
String componentType = getOptionComponentType(selectOption);
TagAttributes attrs = helper.getTagAttributes(selectOption,
getOptionProperties(ctx, widget, selectOption));
return helper.getHtmlComponentHandler(widget.getTagConfigId(), attrs,
nextHandler, componentType, null);
}
// not impacted by custom behaviour by default
protected FaceletHandler getBareOptionFaceletHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
WidgetSelectOption selectOption, FaceletHandler nextHandler) {
String componentType = getOptionComponentType(selectOption);
TagAttributes attrs = helper.getTagAttributes(selectOption);
return helper.getHtmlComponentHandler(widget.getTagConfigId(), attrs,
nextHandler, componentType, null);
}
/**
* Adds a default disabled "select a value" option if widget is not
* required.
*
* @since 6.0
*/
protected FaceletHandler getFirstHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
FaceletHandler nextHandler) {
Object doNotDisplay = widget.getProperty(SelectPropertyMappings.notDisplayDefaultOption.name());
if (doNotDisplay != null) {
if (Boolean.TRUE.equals(doNotDisplay)) {
return null;
}
if (doNotDisplay instanceof String) {
Object res = ComponentTagUtils.resolveElExpression(ctx,
(String) doNotDisplay);
if ((res instanceof Boolean && Boolean.TRUE.equals(res))
|| (res instanceof String && Boolean.parseBoolean((String) res))) {
return null;
}
}
}
String bundleName = ctx.getFacesContext().getApplication().getMessageBundle();
String localizedExpression = String.format("#{%s['%s']}", bundleName,
"label.vocabulary.selectValue");
WidgetSelectOption selectOption = new WidgetSelectOptionImpl("", "",
localizedExpression, "", Boolean.FALSE, Boolean.TRUE);
return getBareOptionFaceletHandler(ctx, helper, widget, selectOption,
nextHandler);
}
/**
* Returns true if widget properties should generate a default tag handler
* for select options.
* <p>
* This default implementation requires the selectOptions widget property
* to be filled.
*
* @since 6.0
*/
protected boolean shouldAddWidgetPropsHandler(Widget widget) {
if (widget.getProperties().containsKey(
SelectPropertyMappings.selectOptions.name())) {
return true;
}
return false;
}
/**
* Computes select options from widget properties.
*
* @since 6.0
*/
protected FaceletHandler getWidgetPropsHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
FaceletHandler nextHandler) {
if (shouldAddWidgetPropsHandler(widget)) {
WidgetSelectOptionsImpl selectOption = new WidgetSelectOptionsImpl(
widget.getProperty(SelectPropertyMappings.selectOptions.name()),
(String) widget.getProperty(SelectPropertyMappings.var.name()),
(String) widget.getProperty(SelectPropertyMappings.itemLabel.name()),
(String) widget.getProperty(SelectPropertyMappings.itemValue.name()),
widget.getProperty(SelectPropertyMappings.itemDisabled.name()),
widget.getProperty(SelectPropertyMappings.itemRendered.name()));
return getOptionFaceletHandler(ctx, helper, widget, selectOption,
nextHandler);
}
return null;
}
protected FaceletHandler getOptionsFaceletHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
WidgetSelectOption[] selectOptions) {
FaceletHandler leaf = new LeafFaceletHandler();
List<FaceletHandler> selectItems = new ArrayList<FaceletHandler>();
FaceletHandler firstItem = getFirstHandler(ctx, helper, widget, leaf);
if (firstItem != null) {
selectItems.add(firstItem);
}
FaceletHandler widgetPropsHandler = getWidgetPropsHandler(ctx, helper,
widget, leaf);
if (widgetPropsHandler != null) {
selectItems.add(widgetPropsHandler);
}
if (selectOptions != null && selectOptions.length > 0) {
for (WidgetSelectOption selectOption : selectOptions) {
if (selectOption == null) {
continue;
}
FaceletHandler h = getBareOptionFaceletHandler(ctx, helper,
widget, selectOption, leaf);
if (h != null) {
selectItems.add(h);
}
}
}
return new CompositeFaceletHandler(
selectItems.toArray(new FaceletHandler[0]));
}
protected FaceletHandler getOptionsFaceletHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget) {
return getOptionsFaceletHandler(ctx, helper, widget,
widget.getSelectOptions());
}
/**
* Returns properties useful for select items, not to be reported on the
* select component.
*/
protected List<String> getExcludedProperties() {
List<String> excludedProps = new ArrayList<>();
// BBB
excludedProps.add("cssStyle");
excludedProps.add("cssStyleClass");
for (SelectPropertyMappings mapping : SelectPropertyMappings.values()) {
excludedProps.add(mapping.name());
}
return excludedProps;
}
protected FaceletHandler getFaceletHandler(FaceletContext ctx,
TagConfig tagConfig, Widget widget, FaceletHandler[] subHandlers,
String componentType) throws WidgetException {
return getFaceletHandler(ctx, tagConfig, widget, subHandlers,
componentType, null);
}
protected FaceletHandler getComponentFaceletHandler(FaceletContext ctx,
FaceletHandlerHelper helper, Widget widget,
FaceletHandler componentHandler) {
return componentHandler;
}
protected FaceletHandler getFaceletHandler(FaceletContext ctx,
TagConfig tagConfig, Widget widget, FaceletHandler[] subHandlers,
String componentType, String rendererType) throws WidgetException {
FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, tagConfig);
String mode = widget.getMode();
String widgetId = widget.getId();
String widgetName = widget.getName();
String widgetTagConfigId = widget.getTagConfigId();
List<String> excludedProps = getExcludedProperties();
TagAttributes attributes = helper.getTagAttributes(widget,
excludedProps, true);
// BBB for CSS style classes on directory select components
if (widget.getProperty("cssStyle") != null) {
attributes = FaceletHandlerHelper.addTagAttribute(
attributes,
helper.createAttribute("style",
(String) widget.getProperty("cssStyle")));
}
if (widget.getProperty("cssStyleClass") != null) {
attributes = FaceletHandlerHelper.addTagAttribute(
attributes,
helper.createAttribute("styleClass",
(String) widget.getProperty("cssStyleClass")));
}
if (!BuiltinWidgetModes.isLikePlainMode(mode)) {
attributes = FaceletHandlerHelper.addTagAttribute(attributes,
helper.createAttribute("id", widgetId));
}
if (BuiltinWidgetModes.EDIT.equals(mode)) {
FaceletHandler optionsHandler = getOptionsFaceletHandler(ctx,
helper, widget);
FaceletHandler[] nextHandlers = new FaceletHandler[] {};
nextHandlers = (FaceletHandler[]) ArrayUtils.add(nextHandlers,
optionsHandler);
if (subHandlers != null) {
nextHandlers = (FaceletHandler[]) ArrayUtils.addAll(
nextHandlers, subHandlers);
}
FaceletHandler leaf = getNextHandler(ctx, tagConfig, widget,
nextHandlers, helper, true);
// maybe add convert handler for easier integration of select2
// widgets handling multiple values
if (HtmlSelectManyListbox.COMPONENT_TYPE.equals(componentType)
|| HtmlSelectManyCheckbox.COMPONENT_TYPE.equals(componentType)
|| HtmlSelectManyMenu.COMPONENT_TYPE.equals(componentType)) {
// add hint for value conversion to collection
attributes = FaceletHandlerHelper.addTagAttribute(attributes,
helper.createAttribute("collectionType",
ArrayList.class.getName()));
}
ComponentHandler input = helper.getHtmlComponentHandler(
widgetTagConfigId, attributes, leaf, componentType,
rendererType);
String msgId = helper.generateMessageId(widgetName);
ComponentHandler message = helper.getMessageComponentHandler(
widgetTagConfigId, msgId, widgetId, null);
FaceletHandler[] handlers = {
getComponentFaceletHandler(ctx, helper, widget, input),
message };
return new CompositeFaceletHandler(handlers);
} else {
// TODO
return null;
}
}
}