001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 016 * 017 * $Id: TemplateWidgetTypeHandler.java 28244 2007-12-18 19:44:57Z atchertchian $ 018 */ 019 020package org.nuxeo.ecm.platform.forms.layout.facelets.plugins; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import javax.el.ExpressionFactory; 029import javax.el.ValueExpression; 030import javax.faces.view.facelets.CompositeFaceletHandler; 031import javax.faces.view.facelets.FaceletContext; 032import javax.faces.view.facelets.FaceletHandler; 033import javax.faces.view.facelets.TagAttribute; 034import javax.faces.view.facelets.TagAttributes; 035import javax.faces.view.facelets.TagConfig; 036 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 040import org.nuxeo.ecm.platform.forms.layout.api.Widget; 041import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException; 042import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper; 043import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables; 044import org.nuxeo.ecm.platform.forms.layout.facelets.ValueExpressionHelper; 045import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; 046import org.nuxeo.ecm.platform.ui.web.binding.MapValueExpression; 047import org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler; 048import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory; 049import org.nuxeo.runtime.api.Framework; 050 051import com.sun.faces.facelets.tag.ui.DecorateHandler; 052 053/** 054 * Template widget type. 055 * 056 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 057 */ 058public class TemplateWidgetTypeHandler extends AbstractWidgetTypeHandler { 059 060 private static final Log log = LogFactory.getLog(TemplateWidgetTypeHandler.class); 061 062 private static final long serialVersionUID = 6886289896957398368L; 063 064 public static final String TEMPLATE_PROPERTY_NAME = "template"; 065 066 /** 067 * Property that can be put on the widget type definition to decide whether the widget type should bind to parent 068 * value when no field is set 069 * 070 * @since 5.6 071 */ 072 public static final String BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME = "bindValueIfNoField"; 073 074 @Override 075 public FaceletHandler getFaceletHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget, 076 FaceletHandler[] subHandlers) throws WidgetException { 077 String template = getTemplateValue(widget); 078 FaceletHandler leaf = new LeafFaceletHandler(); 079 if (template == null) { 080 log.error("Missing template property for widget " + widget.getName() + " in layout " 081 + widget.getLayoutName()); 082 return leaf; 083 } 084 FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, tagConfig); 085 String widgetId = widget.getId(); 086 TagAttributes attributes = helper.getTagAttributes(widgetId, widget); 087 TagAttribute templateAttr = getTemplateAttribute(helper); 088 if (templateAttr == null) { 089 templateAttr = helper.createAttribute(TEMPLATE_PROPERTY_NAME, template); 090 } 091 attributes = FaceletHandlerHelper.addTagAttribute(attributes, templateAttr); 092 String widgetTagConfigId = widget.getTagConfigId(); 093 FaceletHandler nextHandler = leaf; 094 if (subHandlers != null) { 095 nextHandler = new CompositeFaceletHandler(subHandlers); 096 } 097 098 TagConfig config = TagConfigFactory.createTagConfig(tagConfig, widgetTagConfigId, attributes, nextHandler); 099 100 Map<String, ValueExpression> variables = getVariablesForRendering(ctx, helper, widget, subHandlers, 101 widgetTagConfigId, template); 102 103 List<String> blockedPatterns = new ArrayList<String>(); 104 blockedPatterns.add(RenderVariables.widgetVariables.field.name() + "*"); 105 blockedPatterns.add(RenderVariables.widgetVariables.fieldOrValue.name()); 106 blockedPatterns.add(RenderVariables.widgetVariables.widgetProperty.name() + "_*"); 107 blockedPatterns.add(RenderVariables.widgetVariables.widgetProperties.name()); 108 blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name() + "_*"); 109 110 DecorateHandler includeHandler = new DecorateHandler(config); 111 FaceletHandler handler = helper.getAliasTagHandler(widgetTagConfigId, variables, blockedPatterns, 112 includeHandler); 113 return handler; 114 } 115 116 /** 117 * Computes variables for rendering, making available the field values in templates using the format "field_0", 118 * "field_1", etc. and also the widget properties using the format "widgetProperty_thePropertyName". 119 */ 120 protected Map<String, ValueExpression> getVariablesForRendering(FaceletContext ctx, FaceletHandlerHelper helper, 121 Widget widget, FaceletHandler[] subHandlers, String widgetTagConfigId, String template) { 122 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 123 ExpressionFactory eFactory = ctx.getExpressionFactory(); 124 125 FieldDefinition[] fieldDefs = widget.getFieldDefinitions(); 126 // expose field variables 127 FieldDefinition firstField = null; 128 if (fieldDefs != null && fieldDefs.length > 0) { 129 for (int i = 0; i < fieldDefs.length; i++) { 130 if (i == 0) { 131 addFieldVariable(variables, ctx, widget, fieldDefs[i], null); 132 firstField = fieldDefs[i]; 133 } 134 addFieldVariable(variables, ctx, widget, fieldDefs[i], Integer.valueOf(i)); 135 } 136 } else if (getBindValueIfNoFieldValue(widget)) { 137 // expose value as first parameter 138 addFieldVariable(variables, ctx, widget, null, null); 139 addFieldVariable(variables, ctx, widget, null, Integer.valueOf(0)); 140 } 141 142 // add binding "fieldOrValue" available since 5.6, in case template 143 // widget is always supposed to bind value when no field is defined 144 String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), firstField); 145 variables.put(RenderVariables.widgetVariables.fieldOrValue.name(), 146 eFactory.createValueExpression(ctx, computedValue, Object.class)); 147 148 // expose widget properties too 149 WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); 150 Map<String, ValueExpression> mappedExpressions = new HashMap<String, ValueExpression>(); 151 for (Map.Entry<String, Serializable> prop : widget.getProperties().entrySet()) { 152 String key = prop.getKey(); 153 String name = String.format("%s_%s", RenderVariables.widgetVariables.widgetProperty.name(), key); 154 String value; 155 Serializable valueInstance = prop.getValue(); 156 if (!layoutService.referencePropertyAsExpression(key, valueInstance, widget.getType(), 157 widget.getTypeCategory(), widget.getMode(), template)) { 158 // FIXME: this will not be updated correctly using ajax 159 value = (String) valueInstance; 160 } else { 161 // create a reference so that it's a real expression and it's 162 // not kept (cached) in a component value on ajax refresh 163 value = String.format("#{%s.properties.%s}", RenderVariables.widgetVariables.widget.name(), key); 164 } 165 ValueExpression ve = eFactory.createValueExpression(ctx, value, Object.class); 166 variables.put(name, ve); 167 mappedExpressions.put(key, ve); 168 } 169 variables.put(RenderVariables.widgetVariables.widgetProperties.name(), 170 new MapValueExpression(mappedExpressions)); 171 // expose widget controls too 172 for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) { 173 String key = ctrl.getKey(); 174 String name = String.format("%s_%s", RenderVariables.widgetVariables.widgetControl.name(), key); 175 String value = String.format("#{%s.controls.%s}", RenderVariables.widgetVariables.widget.name(), key); 176 variables.put(name, eFactory.createValueExpression(ctx, value, Object.class)); 177 } 178 return variables; 179 } 180 181 protected void addFieldVariable(Map<String, ValueExpression> variables, FaceletContext ctx, Widget widget, 182 FieldDefinition fieldDef, Integer index) { 183 String computedName; 184 if (index == null) { 185 computedName = RenderVariables.widgetVariables.field.name(); 186 } else { 187 computedName = String.format("%s_%s", RenderVariables.widgetVariables.field.name(), index); 188 } 189 String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), fieldDef); 190 191 ExpressionFactory eFactory = ctx.getExpressionFactory(); 192 variables.put(computedName, eFactory.createValueExpression(ctx, computedValue, Object.class)); 193 } 194 195 /** 196 * Returns the "template" property value, looking up on the widget definition definition first, and on the widget 197 * type definition if not found. 198 */ 199 protected String getTemplateValue(Widget widget) { 200 return lookupProperty(TEMPLATE_PROPERTY_NAME, widget); 201 } 202 203 /** 204 * Helper method to retrieve a property value, looking up on the widget definition first, and on the widget type 205 * definition if not found. 206 * 207 * @since 7.2 208 */ 209 protected String lookupProperty(String name, Widget widget) { 210 // lookup in the widget configuration 211 String val = (String) widget.getProperty(name); 212 if (val == null) { 213 // lookup in the widget type configuration 214 val = getProperty(name); 215 } 216 return val; 217 } 218 219 /** 220 * Returns the "bindValueIfNoField" property value, looking up on the widget type definition first, and on the 221 * widget definition if not found. 222 * 223 * @since 5.6 224 * @param widget 225 * @return 226 */ 227 protected boolean getBindValueIfNoFieldValue(Widget widget) { 228 Object value = getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME); 229 if (value == null) { 230 value = widget.getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME); 231 } 232 if (value == null) { 233 return false; 234 } 235 if (value instanceof Boolean) { 236 return Boolean.TRUE.equals(value); 237 } 238 return Boolean.TRUE.equals(Boolean.valueOf(value.toString())); 239 240 } 241 242 /** 243 * Returns the template attribute. 244 */ 245 protected TagAttribute getTemplateAttribute(FaceletHandlerHelper helper) { 246 // do not return anything as it will be computed from the widget 247 // properties anyway. 248 return null; 249 } 250 251}