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: WidgetTypeTagHandler.java 26053 2007-10-16 01:45:43Z atchertchian $ 018 */ 019 020package org.nuxeo.ecm.platform.forms.layout.facelets; 021 022import java.io.IOException; 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import javax.el.ELException; 031import javax.el.ValueExpression; 032import javax.el.VariableMapper; 033import javax.faces.component.UIComponent; 034import javax.faces.view.facelets.ComponentConfig; 035import javax.faces.view.facelets.FaceletContext; 036import javax.faces.view.facelets.FaceletHandler; 037import javax.faces.view.facelets.TagAttribute; 038import javax.faces.view.facelets.TagConfig; 039import javax.faces.view.facelets.TagHandler; 040 041import org.apache.commons.lang.StringUtils; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 045import org.nuxeo.ecm.platform.forms.layout.api.Widget; 046import org.nuxeo.ecm.platform.forms.layout.api.impl.FieldDefinitionImpl; 047import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; 048import org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler; 049import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; 050import org.nuxeo.ecm.platform.ui.web.tag.handler.SetTagHandler; 051import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory; 052import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 053import org.nuxeo.runtime.api.Framework; 054 055import com.sun.faces.facelets.el.VariableMapperWrapper; 056 057/** 058 * Widget type tag handler. 059 * <p> 060 * Applies a {@link WidgetTypeHandler} resolved from a widget created for given type name and mode, and uses other tag 061 * attributes to fill the widget properties. 062 * <p> 063 * Does not handle sub widgets. 064 * 065 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 066 */ 067public class WidgetTypeTagHandler extends TagHandler { 068 069 private static final Log log = LogFactory.getLog(WidgetTypeTagHandler.class); 070 071 protected final TagConfig config; 072 073 protected final TagAttribute name; 074 075 protected final TagAttribute category; 076 077 protected final TagAttribute mode; 078 079 protected final TagAttribute value; 080 081 protected final TagAttribute field; 082 083 protected final TagAttribute fields; 084 085 protected final TagAttribute label; 086 087 protected final TagAttribute helpLabel; 088 089 protected final TagAttribute translated; 090 091 protected final TagAttribute properties; 092 093 /** 094 * @since 5.7 095 */ 096 protected final TagAttribute widgetName; 097 098 /** 099 * Convenient attribute to remove the "template" property from widget properties (and avoid stack overflow errors 100 * when using another widget type in a widget template, for compatibility code for instance). 101 * 102 * @since 5.6 103 */ 104 protected final TagAttribute ignoreTemplateProperty; 105 106 /** 107 * @since 5.6 108 */ 109 protected final TagAttribute subWidgets; 110 111 protected final TagAttribute resolveOnly; 112 113 protected final TagAttribute[] vars; 114 115 protected final String[] reservedVarsArray = { "id", "name", "category", "mode", "value", "type", "field", 116 "fields", "widgetName", "label", "helpLabel", "translated", "properties", "ignoreTemplateProperty", 117 "subWidgets", "resolveOnly" }; 118 119 public WidgetTypeTagHandler(TagConfig config) { 120 super(config); 121 this.config = config; 122 name = getRequiredAttribute("name"); 123 category = getAttribute("category"); 124 mode = getRequiredAttribute("mode"); 125 value = getAttribute("value"); 126 field = getAttribute("field"); 127 fields = getAttribute("fields"); 128 widgetName = getAttribute("widgetName"); 129 label = getAttribute("label"); 130 helpLabel = getAttribute("helpLabel"); 131 translated = getAttribute("translated"); 132 properties = getAttribute("properties"); 133 ignoreTemplateProperty = getAttribute("ignoreTemplateProperty"); 134 subWidgets = getAttribute("subWidgets"); 135 resolveOnly = getAttribute("resolveOnly"); 136 vars = tag.getAttributes().getAll(); 137 } 138 139 @Override 140 @SuppressWarnings({ "unchecked", "rawtypes" }) 141 public final void apply(FaceletContext ctx, UIComponent parent) throws IOException, ELException { 142 WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); 143 144 // compute field definitions 145 List<FieldDefinition> fieldsValue = new ArrayList<FieldDefinition>(); 146 if (field != null) { 147 Object fieldValue = field.getObject(ctx, Object.class); 148 if (fieldValue instanceof FieldDefinition) { 149 fieldsValue.add((FieldDefinition) fieldValue); 150 } else if (fieldValue instanceof String) { 151 fieldsValue.add(new FieldDefinitionImpl(null, (String) fieldValue)); 152 } else { 153 fieldsValue.add(new FieldDefinitionImpl(null, field.getValue())); 154 } 155 } 156 if (fields != null) { 157 List resolvedfields = (List) fields.getObject(ctx, List.class); 158 for (Object item : resolvedfields) { 159 if (item instanceof FieldDefinition) { 160 fieldsValue.add((FieldDefinition) item); 161 } else if (item instanceof String) { 162 fieldsValue.add(new FieldDefinitionImpl(null, (String) item)); 163 } else { 164 log.error("Invalid field item => discard: " + item); 165 } 166 } 167 } 168 169 // build handler 170 List<String> reservedVars = Arrays.asList(reservedVarsArray); 171 Map<String, Serializable> widgetProps = new HashMap<String, Serializable>(); 172 if (properties != null) { 173 Map<String, Serializable> propertiesValue = (Map<String, Serializable>) properties.getObject(ctx, Map.class); 174 if (propertiesValue != null) { 175 widgetProps.putAll(propertiesValue); 176 } 177 } 178 179 // do not propagate value the value attribute to the widget 180 // properties if field definitions should be taken into account 181 // instead 182 String widgetPropertyMarker = RenderVariables.widgetVariables.widgetProperty.name() + "_"; 183 boolean includeValueInProps = fieldsValue.isEmpty(); 184 for (TagAttribute var : vars) { 185 String localName = var.getLocalName(); 186 if ((!reservedVars.contains(localName)) || ("value".equals(localName) && includeValueInProps)) { 187 String varName = localName; 188 if (localName != null && localName.startsWith(widgetPropertyMarker)) { 189 varName = localName.substring(widgetPropertyMarker.length()); 190 } 191 widgetProps.put(varName, var.getValue()); 192 } 193 } 194 195 boolean ignoreTemplatePropValue = false; 196 if (ignoreTemplateProperty != null) { 197 ignoreTemplatePropValue = ignoreTemplateProperty.getBoolean(ctx); 198 } 199 if (ignoreTemplatePropValue) { 200 widgetProps.remove(TemplateWidgetTypeHandler.TEMPLATE_PROPERTY_NAME); 201 } 202 203 String typeValue = name.getValue(ctx); 204 String categoryValue = null; 205 if (category != null) { 206 categoryValue = category.getValue(ctx); 207 } 208 String modeValue = mode.getValue(ctx); 209 String valueName = null; 210 if (value != null) { 211 valueName = value.getValue(); 212 if (ComponentTagUtils.isStrictValueReference(valueName)) { 213 valueName = ComponentTagUtils.getBareValueName(valueName); 214 } 215 } 216 String widgetNameValue = null; 217 if (widgetName != null) { 218 widgetNameValue = widgetName.getValue(ctx); 219 } 220 String labelValue = null; 221 if (label != null) { 222 labelValue = label.getValue(ctx); 223 } 224 String helpLabelValue = null; 225 if (helpLabel != null) { 226 helpLabelValue = helpLabel.getValue(ctx); 227 } 228 Boolean translatedValue = Boolean.FALSE; 229 if (translated != null) { 230 translatedValue = Boolean.valueOf(translated.getBoolean(ctx)); 231 } 232 233 Widget[] subWidgetsValue = null; 234 if (subWidgets != null) { 235 subWidgetsValue = (Widget[]) subWidgets.getObject(ctx, Widget[].class); 236 } 237 238 // avoid double markers 239 if (widgetNameValue != null && widgetNameValue.startsWith(FaceletHandlerHelper.WIDGET_ID_PREFIX)) { 240 widgetNameValue = widgetNameValue.substring(FaceletHandlerHelper.WIDGET_ID_PREFIX.length()); 241 } 242 if (StringUtils.isBlank(widgetNameValue)) { 243 widgetNameValue = typeValue; 244 } 245 WidgetDefinitionImpl wDef = new WidgetDefinitionImpl(widgetNameValue, typeValue, labelValue, helpLabelValue, 246 translatedValue.booleanValue(), null, fieldsValue, widgetProps, null); 247 wDef.setTypeCategory(categoryValue); 248 wDef.setDynamic(true); 249 Widget widget = layoutService.createWidget(ctx, wDef, modeValue, valueName, subWidgetsValue); 250 251 // expose widget variable 252 VariableMapper orig = ctx.getVariableMapper(); 253 VariableMapper vm = new VariableMapperWrapper(orig); 254 ctx.setVariableMapper(vm); 255 ValueExpression widgetVe = ctx.getExpressionFactory().createValueExpression(widget, Widget.class); 256 vm.setVariable(RenderVariables.widgetVariables.widget.name(), widgetVe); 257 vm.setVariable( 258 String.format("%s_%s", RenderVariables.widgetVariables.widget.name(), 259 Integer.valueOf(widget.getLevel())), widgetVe); 260 // TODO NXP-13280: expose widget controls too when they can be 261 // retrieved from tag attributes 262 try { 263 // set unique id on widget before exposing it to the context 264 FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, config); 265 WidgetTagHandler.generateWidgetId(helper, widget, false); 266 267 boolean resolveOnlyBool = false; 268 if (resolveOnly != null) { 269 resolveOnlyBool = resolveOnly.getBoolean(ctx); 270 } 271 if (resolveOnlyBool) { 272 // NXP-12882: wrap handler in an nxu:set tag to avoid duplicate 273 // id issue when widget definition changes, as component ids 274 // can be cached and not generated-again on ajax re-render, 275 // this is a quick fix that can be optimized, as the widget 276 // variable is already exposed in the current variable mapper. 277 // Update after NXP-15050: this does not seem to be necessary 278 // anymore, could not reproduce the corresponding bug, to 279 // remove after complementary tests. 280 String setTagConfigId = widget.getTagConfigId(); 281 ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig(this.config, setTagConfigId, 282 RenderVariables.widgetVariables.widget.name(), "#{widget}", "true", "true", nextHandler); 283 FaceletHandler handler = new SetTagHandler(aliasConfig); 284 handler.apply(ctx, parent); 285 } else { 286 WidgetTagHandler.applyWidgetHandler(ctx, parent, config, widget, value, true, nextHandler); 287 } 288 } finally { 289 ctx.setVariableMapper(orig); 290 } 291 } 292 293}