/*
* (C) Copyright 2006-2007 Nuxeo SAS (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:
* <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*
* $Id: TemplateWidgetTypeHandler.java 28244 2007-12-18 19:44:57Z atchertchian $
*/
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.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.view.facelets.CompositeFaceletHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.FaceletHandler;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagAttributes;
import javax.faces.view.facelets.TagConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition;
import org.nuxeo.ecm.platform.forms.layout.api.Widget;
import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException;
import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper;
import org.nuxeo.ecm.platform.forms.layout.facelets.LeafFaceletHandler;
import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables;
import org.nuxeo.ecm.platform.forms.layout.facelets.ValueExpressionHelper;
import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager;
import org.nuxeo.ecm.platform.ui.web.binding.MapValueExpression;
import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory;
import org.nuxeo.runtime.api.Framework;
import com.sun.faces.facelets.tag.ui.DecorateHandler;
/**
* Template widget type.
*
* @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*/
public class TemplateWidgetTypeHandler extends AbstractWidgetTypeHandler {
private static final Log log = LogFactory.getLog(TemplateWidgetTypeHandler.class);
private static final long serialVersionUID = 6886289896957398368L;
public static final String TEMPLATE_PROPERTY_NAME = "template";
/**
* Property that can be put on the widget type definition to decide whether
* the widget type should bind to parent value when no field is set
*
* @since 5.6
*/
public static final String BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME = "bindValueIfNoField";
@Override
public FaceletHandler getFaceletHandler(FaceletContext ctx,
TagConfig tagConfig, Widget widget, FaceletHandler[] subHandlers)
throws WidgetException {
String template = getTemplateValue(widget);
FaceletHandler leaf = new LeafFaceletHandler();
if (template == null) {
log.error("Missing template property for widget "
+ widget.getName() + " in layout " + widget.getLayoutName());
return leaf;
}
FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, tagConfig);
String widgetId = widget.getId();
TagAttributes attributes = helper.getTagAttributes(widgetId, widget);
TagAttribute templateAttr = getTemplateAttribute(helper);
if (templateAttr == null) {
templateAttr = helper.createAttribute(TEMPLATE_PROPERTY_NAME,
template);
}
attributes = FaceletHandlerHelper.addTagAttribute(attributes,
templateAttr);
String widgetTagConfigId = widget.getTagConfigId();
FaceletHandler nextHandler = leaf;
if (subHandlers != null) {
nextHandler = new CompositeFaceletHandler(subHandlers);
}
TagConfig config = TagConfigFactory.createTagConfig(tagConfig,
widgetTagConfigId, attributes, nextHandler);
Map<String, ValueExpression> variables = getVariablesForRendering(ctx,
helper, widget, subHandlers, widgetTagConfigId, template);
List<String> blockedPatterns = new ArrayList<String>();
blockedPatterns.add(RenderVariables.widgetVariables.field.name() + "*");
blockedPatterns.add(RenderVariables.widgetVariables.fieldOrValue.name());
blockedPatterns.add(RenderVariables.widgetVariables.widgetProperty.name()
+ "_*");
blockedPatterns.add(RenderVariables.widgetVariables.widgetProperties.name());
blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name()
+ "_*");
DecorateHandler includeHandler = new DecorateHandler(config);
FaceletHandler handler = helper.getAliasTagHandler(widgetTagConfigId,
variables, blockedPatterns, includeHandler);
return handler;
}
/**
* Computes variables for rendering, making available the field values in
* templates using the format "field_0", "field_1", etc. and also the
* widget properties using the format "widgetProperty_thePropertyName".
*/
protected Map<String, ValueExpression> getVariablesForRendering(
FaceletContext ctx, FaceletHandlerHelper helper, Widget widget,
FaceletHandler[] subHandlers, String widgetTagConfigId,
String template) {
Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>();
ExpressionFactory eFactory = ctx.getExpressionFactory();
FieldDefinition[] fieldDefs = widget.getFieldDefinitions();
// expose field variables
FieldDefinition firstField = null;
if (fieldDefs != null && fieldDefs.length > 0) {
for (int i = 0; i < fieldDefs.length; i++) {
if (i == 0) {
addFieldVariable(variables, ctx, widget, fieldDefs[i], null);
firstField = fieldDefs[i];
}
addFieldVariable(variables, ctx, widget, fieldDefs[i],
Integer.valueOf(i));
}
} else if (getBindValueIfNoFieldValue(widget)) {
// expose value as first parameter
addFieldVariable(variables, ctx, widget, null, null);
addFieldVariable(variables, ctx, widget, null, Integer.valueOf(0));
}
// add binding "fieldOrValue" available since 5.6, in case template
// widget is always supposed to bind value when no field is defined
String computedValue = ValueExpressionHelper.createExpressionString(
widget.getValueName(), firstField);
variables.put(
RenderVariables.widgetVariables.fieldOrValue.name(),
eFactory.createValueExpression(ctx, computedValue, Object.class));
// expose widget properties too
WebLayoutManager layoutService;
try {
layoutService = Framework.getService(WebLayoutManager.class);
} catch (Exception e) {
throw new FacesException(e);
}
if (layoutService == null) {
throw new FacesException("Layout service not found");
}
Map<String, ValueExpression> mappedExpressions = new HashMap<String, ValueExpression>();
for (Map.Entry<String, Serializable> prop : widget.getProperties().entrySet()) {
String key = prop.getKey();
String name = String.format("%s_%s",
RenderVariables.widgetVariables.widgetProperty.name(), key);
String value;
Serializable valueInstance = prop.getValue();
if (!layoutService.referencePropertyAsExpression(key,
valueInstance, widget.getType(), widget.getTypeCategory(),
widget.getMode(), template)) {
// FIXME: this will not be updated correctly using ajax
value = (String) valueInstance;
} else {
// create a reference so that it's a real expression and it's
// not kept (cached) in a component value on ajax refresh
value = String.format("#{%s.properties.%s}",
RenderVariables.widgetVariables.widget.name(), key);
}
ValueExpression ve = eFactory.createValueExpression(ctx, value,
Object.class);
variables.put(name, ve);
mappedExpressions.put(key, ve);
}
variables.put(RenderVariables.widgetVariables.widgetProperties.name(),
new MapValueExpression(mappedExpressions));
// expose widget controls too
for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) {
String key = ctrl.getKey();
String name = String.format("%s_%s",
RenderVariables.widgetVariables.widgetControl.name(), key);
String value = String.format("#{%s.controls.%s}",
RenderVariables.widgetVariables.widget.name(), key);
variables.put(name,
eFactory.createValueExpression(ctx, value, Object.class));
}
return variables;
}
protected void addFieldVariable(Map<String, ValueExpression> variables,
FaceletContext ctx, Widget widget, FieldDefinition fieldDef,
Integer index) {
String computedName;
if (index == null) {
computedName = RenderVariables.widgetVariables.field.name();
} else {
computedName = String.format("%s_%s",
RenderVariables.widgetVariables.field.name(), index);
}
String computedValue = ValueExpressionHelper.createExpressionString(
widget.getValueName(), fieldDef);
ExpressionFactory eFactory = ctx.getExpressionFactory();
variables.put(
computedName,
eFactory.createValueExpression(ctx, computedValue, Object.class));
}
/**
* Returns the "template" property value, looking up on the widget type
* definition first, and on the widget definition if not found.
*/
protected String getTemplateValue(Widget widget) {
// lookup in the widget type configuration
String template = getProperty(TEMPLATE_PROPERTY_NAME);
if (template == null) {
// lookup in the widget configuration
template = (String) widget.getProperty(TEMPLATE_PROPERTY_NAME);
}
return template;
}
/**
* Returns the "bindValueIfNoField" property value, looking up on the
* widget type definition first, and on the widget definition if not found.
*
* @since 5.6
* @param widget
* @return
*/
protected boolean getBindValueIfNoFieldValue(Widget widget) {
Object value = getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME);
if (value == null) {
value = widget.getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME);
}
if (value == null) {
return false;
}
if (value instanceof Boolean) {
return Boolean.TRUE.equals(value);
}
return Boolean.TRUE.equals(Boolean.valueOf(value.toString()));
}
/**
* Returns the template attribute.
*/
protected TagAttribute getTemplateAttribute(FaceletHandlerHelper helper) {
// do not return anything as it will be computed from the widget
// properties anyway.
return null;
}
}