/*
* (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 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.IOException;
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.component.UIComponent;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagAttributes;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
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.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.ecm.platform.ui.web.util.FaceletDebugTracer;
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);
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";
public TemplateWidgetTypeHandler(TagConfig config) {
super(config);
}
@Override
public void apply(FaceletContext ctx, UIComponent parent, Widget widget) throws WidgetException, IOException {
long start = FaceletDebugTracer.start();
try {
String template = getTemplateValue(widget);
if (template == null) {
log.error("Missing template property for widget " + widget.getName() + " in layout "
+ widget.getLayoutName());
return;
}
FaceletHandlerHelper helper = new FaceletHandlerHelper(tagConfig);
TagAttribute templateAttr = getTemplateAttribute(helper);
if (templateAttr == null) {
templateAttr = helper.createAttribute(TEMPLATE_PROPERTY_NAME, template);
}
TagAttributes attributes = FaceletHandlerHelper.getTagAttributes(templateAttr);
String widgetTagConfigId = widget.getTagConfigId();
TagConfig config = TagConfigFactory.createTagConfig(tagConfig, widgetTagConfigId, attributes, nextHandler);
Map<String, ValueExpression> variables = getVariablesForRendering(ctx, helper, widget, 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);
TagHandler handler = helper.getAliasTagHandler(widgetTagConfigId, variables, blockedPatterns,
includeHandler);
handler.apply(ctx, parent);
} finally {
FaceletDebugTracer.trace(start, tagConfig.getTag(), widget.getId(), -1);
}
}
/**
* 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, 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 = Framework.getService(WebLayoutManager.class);
Map<String, ValueExpression> mappedExpressions = new HashMap<String, ValueExpression>();
for (Map.Entry<String, Serializable> prop : widget.getProperties().entrySet()) {
String key = prop.getKey();
String name = 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 = "#{" + RenderVariables.widgetVariables.widget.name() + ".properties." + 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 = RenderVariables.widgetVariables.widgetControl.name() + "_" + key;
String value = "#{" + RenderVariables.widgetVariables.widget.name() + ".controls." + 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 = 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 definition definition first, and on the widget
* type definition if not found.
*/
protected String getTemplateValue(Widget widget) {
return lookupProperty(TEMPLATE_PROPERTY_NAME, widget);
}
/**
* Helper method to retrieve a property value, looking up on the widget definition first, and on the widget type
* definition if not found.
*
* @since 7.2
*/
protected String lookupProperty(String name, Widget widget) {
// lookup in the widget configuration
String val = (String) widget.getProperty(name);
if (val == null) {
// lookup in the widget type configuration
val = getProperty(name);
}
return val;
}
/**
* 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;
}
}