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