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