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.runtime.api.Framework; 052 053import com.sun.faces.facelets.tag.ui.DecorateHandler; 054 055/** 056 * Template widget type. 057 * 058 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 059 */ 060public class TemplateWidgetTypeHandler extends AbstractWidgetTypeHandler { 061 062 private static final Log log = LogFactory.getLog(TemplateWidgetTypeHandler.class); 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 public TemplateWidgetTypeHandler(TagConfig config) { 075 super(config); 076 } 077 078 @Override 079 public void apply(FaceletContext ctx, UIComponent parent, Widget widget) throws WidgetException, IOException { 080 String template = getTemplateValue(widget); 081 if (template == null) { 082 log.error("Missing template property for widget " + widget.getName() + " in layout " 083 + widget.getLayoutName()); 084 return; 085 } 086 FaceletHandlerHelper helper = new FaceletHandlerHelper(tagConfig); 087 String widgetId = widget.getId(); 088 TagAttributes attributes = helper.getTagAttributes(widgetId, widget); 089 TagAttribute templateAttr = getTemplateAttribute(helper); 090 if (templateAttr == null) { 091 templateAttr = helper.createAttribute(TEMPLATE_PROPERTY_NAME, template); 092 } 093 attributes = FaceletHandlerHelper.addTagAttribute(attributes, templateAttr); 094 String widgetTagConfigId = widget.getTagConfigId(); 095 TagConfig config = TagConfigFactory.createTagConfig(tagConfig, widgetTagConfigId, attributes, nextHandler); 096 097 Map<String, ValueExpression> variables = getVariablesForRendering(ctx, helper, widget, widgetTagConfigId, 098 template); 099 100 List<String> blockedPatterns = new ArrayList<String>(); 101 blockedPatterns.add(RenderVariables.widgetVariables.field.name() + "*"); 102 blockedPatterns.add(RenderVariables.widgetVariables.fieldOrValue.name()); 103 blockedPatterns.add(RenderVariables.widgetVariables.widgetProperty.name() + "_*"); 104 blockedPatterns.add(RenderVariables.widgetVariables.widgetProperties.name()); 105 blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name() + "_*"); 106 107 DecorateHandler includeHandler = new DecorateHandler(config); 108 TagHandler handler = helper.getAliasTagHandler(widgetTagConfigId, variables, blockedPatterns, includeHandler); 109 handler.apply(ctx, parent); 110 } 111 112 /** 113 * Computes variables for rendering, making available the field values in templates using the format "field_0", 114 * "field_1", etc. and also the widget properties using the format "widgetProperty_thePropertyName". 115 */ 116 protected Map<String, ValueExpression> getVariablesForRendering(FaceletContext ctx, FaceletHandlerHelper helper, 117 Widget widget, String widgetTagConfigId, String template) { 118 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 119 ExpressionFactory eFactory = ctx.getExpressionFactory(); 120 121 FieldDefinition[] fieldDefs = widget.getFieldDefinitions(); 122 // expose field variables 123 FieldDefinition firstField = null; 124 if (fieldDefs != null && fieldDefs.length > 0) { 125 for (int i = 0; i < fieldDefs.length; i++) { 126 if (i == 0) { 127 addFieldVariable(variables, ctx, widget, fieldDefs[i], null); 128 firstField = fieldDefs[i]; 129 } 130 addFieldVariable(variables, ctx, widget, fieldDefs[i], Integer.valueOf(i)); 131 } 132 } else if (getBindValueIfNoFieldValue(widget)) { 133 // expose value as first parameter 134 addFieldVariable(variables, ctx, widget, null, null); 135 addFieldVariable(variables, ctx, widget, null, Integer.valueOf(0)); 136 } 137 138 // add binding "fieldOrValue" available since 5.6, in case template 139 // widget is always supposed to bind value when no field is defined 140 String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), firstField); 141 variables.put(RenderVariables.widgetVariables.fieldOrValue.name(), 142 eFactory.createValueExpression(ctx, computedValue, Object.class)); 143 144 // expose widget properties too 145 WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); 146 Map<String, ValueExpression> mappedExpressions = new HashMap<String, ValueExpression>(); 147 for (Map.Entry<String, Serializable> prop : widget.getProperties().entrySet()) { 148 String key = prop.getKey(); 149 String name = RenderVariables.widgetVariables.widgetProperty.name() + "_" + key; 150 String value; 151 Serializable valueInstance = prop.getValue(); 152 if (!layoutService.referencePropertyAsExpression(key, valueInstance, widget.getType(), 153 widget.getTypeCategory(), widget.getMode(), template)) { 154 // FIXME: this will not be updated correctly using ajax 155 value = (String) valueInstance; 156 } else { 157 // create a reference so that it's a real expression and it's 158 // not kept (cached) in a component value on ajax refresh 159 value = "#{" + RenderVariables.widgetVariables.widget.name() + ".properties." + key + "}"; 160 } 161 ValueExpression ve = eFactory.createValueExpression(ctx, value, Object.class); 162 variables.put(name, ve); 163 mappedExpressions.put(key, ve); 164 } 165 variables.put(RenderVariables.widgetVariables.widgetProperties.name(), 166 new MapValueExpression(mappedExpressions)); 167 // expose widget controls too 168 for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) { 169 String key = ctrl.getKey(); 170 String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key; 171 String value = "#{" + RenderVariables.widgetVariables.widget.name() + ".controls." + key + "}"; 172 variables.put(name, eFactory.createValueExpression(ctx, value, Object.class)); 173 } 174 return variables; 175 } 176 177 protected void addFieldVariable(Map<String, ValueExpression> variables, FaceletContext ctx, Widget widget, 178 FieldDefinition fieldDef, Integer index) { 179 String computedName; 180 if (index == null) { 181 computedName = RenderVariables.widgetVariables.field.name(); 182 } else { 183 computedName = RenderVariables.widgetVariables.field.name() + "_" + index; 184 } 185 String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), fieldDef); 186 187 ExpressionFactory eFactory = ctx.getExpressionFactory(); 188 variables.put(computedName, eFactory.createValueExpression(ctx, computedValue, Object.class)); 189 } 190 191 /** 192 * Returns the "template" property value, looking up on the widget definition definition first, and on the widget 193 * type definition if not found. 194 */ 195 protected String getTemplateValue(Widget widget) { 196 return lookupProperty(TEMPLATE_PROPERTY_NAME, widget); 197 } 198 199 /** 200 * Helper method to retrieve a property value, looking up on the widget definition first, and on the widget type 201 * definition if not found. 202 * 203 * @since 7.2 204 */ 205 protected String lookupProperty(String name, Widget widget) { 206 // lookup in the widget configuration 207 String val = (String) widget.getProperty(name); 208 if (val == null) { 209 // lookup in the widget type configuration 210 val = getProperty(name); 211 } 212 return val; 213 } 214 215 /** 216 * Returns the "bindValueIfNoField" property value, looking up on the widget type definition first, and on the 217 * widget definition if not found. 218 * 219 * @since 5.6 220 * @param widget 221 * @return 222 */ 223 protected boolean getBindValueIfNoFieldValue(Widget widget) { 224 Object value = getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME); 225 if (value == null) { 226 value = widget.getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME); 227 } 228 if (value == null) { 229 return false; 230 } 231 if (value instanceof Boolean) { 232 return Boolean.TRUE.equals(value); 233 } 234 return Boolean.TRUE.equals(Boolean.valueOf(value.toString())); 235 236 } 237 238 /** 239 * Returns the template attribute. 240 */ 241 protected TagAttribute getTemplateAttribute(FaceletHandlerHelper helper) { 242 // do not return anything as it will be computed from the widget 243 // properties anyway. 244 return null; 245 } 246 247}