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: WidgetTagHandler.java 30553 2008-02-24 15:51:31Z 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.ExpressionFactory; 034import javax.el.ValueExpression; 035import javax.el.VariableMapper; 036import javax.faces.FacesException; 037import javax.faces.component.UIComponent; 038import javax.faces.view.facelets.FaceletContext; 039import javax.faces.view.facelets.FaceletHandler; 040import javax.faces.view.facelets.MetaRuleset; 041import javax.faces.view.facelets.MetaTagHandler; 042import javax.faces.view.facelets.TagAttribute; 043import javax.faces.view.facelets.TagConfig; 044import javax.faces.view.facelets.TagException; 045 046import org.apache.commons.logging.Log; 047import org.apache.commons.logging.LogFactory; 048import org.jboss.el.ValueExpressionLiteral; 049import org.nuxeo.ecm.platform.forms.layout.api.Widget; 050import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; 051import org.nuxeo.ecm.platform.forms.layout.facelets.dev.DevTagHandler; 052import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; 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 tag handler. 061 * <p> 062 * Applies {@link WidgetTypeHandler} found for given widget, in given mode and for given value. 063 * 064 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 065 */ 066public class WidgetTagHandler extends MetaTagHandler { 067 068 private static final Log log = LogFactory.getLog(WidgetTagHandler.class); 069 070 protected final TagConfig config; 071 072 protected final TagAttribute widget; 073 074 /** 075 * @since 5.6 076 */ 077 protected final TagAttribute name; 078 079 /** 080 * @since 5.6 081 */ 082 protected final TagAttribute category; 083 084 /** 085 * @since 5.6 086 */ 087 protected final TagAttribute definition; 088 089 /** 090 * @since 5.6 091 */ 092 protected final TagAttribute mode; 093 094 /** 095 * @since 5.6 096 */ 097 protected final TagAttribute layoutName; 098 099 /** 100 * @since 5.7 101 */ 102 protected final TagAttribute resolveOnly; 103 104 protected final TagAttribute value; 105 106 protected final TagAttribute[] vars; 107 108 protected final String[] reservedVarsArray = { "id", "widget", "name", "category", "definition", "mode", 109 "layoutName", "value", "resolveOnly" }; 110 111 public WidgetTagHandler(TagConfig config) { 112 super(config); 113 this.config = config; 114 115 widget = getAttribute("widget"); 116 name = getAttribute("name"); 117 definition = getAttribute("definition"); 118 category = getAttribute("category"); 119 mode = getAttribute("mode"); 120 layoutName = getAttribute("layoutName"); 121 resolveOnly = getAttribute("resolveOnly"); 122 123 value = getAttribute("value"); 124 vars = tag.getAttributes().getAll(); 125 126 // additional checks 127 if (name == null && widget == null && definition == null) { 128 throw new TagException(this.tag, "At least one of attributes 'name', 'widget' " 129 + "or 'definition' is required"); 130 } 131 if (widget == null && (name != null || definition != null)) { 132 if (mode == null) { 133 throw new TagException(this.tag, "Attribute 'mode' is required when using attribute" 134 + " 'name' or 'definition' so that the " + "widget instance " + "can be resolved"); 135 } 136 } 137 } 138 139 /** 140 * Renders given widget resolving its {@link FaceletHandler} from {@link WebLayoutManager} configuration. 141 * <p> 142 * Variables exposed: {@link RenderVariables.globalVariables#value}, same variable suffixed with "_n" where n is the 143 * widget level, and {@link RenderVariables.globalVariables#document}. 144 */ 145 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { 146 // compute value name to set on widget instance in case it's changed 147 // from first computation 148 String valueName = null; 149 if (value != null) { 150 valueName = value.getValue(); 151 } 152 if (ComponentTagUtils.isStrictValueReference(valueName)) { 153 valueName = ComponentTagUtils.getBareValueName(valueName); 154 } 155 156 // build handler 157 boolean widgetInstanceBuilt = false; 158 Widget widgetInstance = null; 159 if (widget != null) { 160 widgetInstance = (Widget) widget.getObject(ctx, Widget.class); 161 if (widgetInstance != null && valueName != null) { 162 widgetInstance.setValueName(valueName); 163 } 164 } else { 165 // resolve widget according to name and mode (and optional 166 // category) 167 WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); 168 169 String modeValue = mode.getValue(ctx); 170 String layoutNameValue = null; 171 if (layoutName != null) { 172 layoutNameValue = layoutName.getValue(ctx); 173 } 174 175 if (name != null) { 176 String nameValue = name.getValue(ctx); 177 String catValue = null; 178 if (category != null) { 179 catValue = category.getValue(ctx); 180 } 181 widgetInstance = layoutService.getWidget(ctx, nameValue, catValue, modeValue, valueName, 182 layoutNameValue); 183 widgetInstanceBuilt = true; 184 } else if (definition != null) { 185 WidgetDefinition widgetDef = (WidgetDefinition) definition.getObject(ctx, WidgetDefinition.class); 186 if (widgetDef != null) { 187 widgetInstance = layoutService.getWidget(ctx, widgetDef, modeValue, valueName, layoutNameValue); 188 widgetInstanceBuilt = true; 189 } 190 } 191 192 } 193 if (widgetInstance != null) { 194 // add additional properties put on tag 195 String widgetPropertyMarker = RenderVariables.widgetVariables.widgetProperty.name() + "_"; 196 List<String> reservedVars = Arrays.asList(reservedVarsArray); 197 for (TagAttribute var : vars) { 198 String localName = var.getLocalName(); 199 if (!reservedVars.contains(localName)) { 200 if (localName != null && localName.startsWith(widgetPropertyMarker)) { 201 localName = localName.substring(widgetPropertyMarker.length()); 202 } 203 widgetInstance.setProperty(localName, var.getValue()); 204 } 205 } 206 VariableMapper orig = ctx.getVariableMapper(); 207 if (widgetInstanceBuilt) { 208 // expose widget variable to the context as layout row has not done it already, and set unique id on 209 // widget before exposing it to the context 210 FaceletHandlerHelper helper = new FaceletHandlerHelper(config); 211 WidgetTagHandler.generateWidgetId(ctx, helper, widgetInstance, false); 212 213 VariableMapper vm = new VariableMapperWrapper(orig); 214 ctx.setVariableMapper(vm); 215 ExpressionFactory eFactory = ctx.getExpressionFactory(); 216 ValueExpression widgetVe = eFactory.createValueExpression(widgetInstance, Widget.class); 217 vm.setVariable(RenderVariables.widgetVariables.widget.name(), widgetVe); 218 // expose widget controls too 219 for (Map.Entry<String, Serializable> ctrl : widgetInstance.getControls().entrySet()) { 220 String key = ctrl.getKey(); 221 String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key; 222 String value = "#{" + RenderVariables.widgetVariables.widget.name() + ".controls." + key + "}"; 223 vm.setVariable(name, eFactory.createValueExpression(ctx, value, Object.class)); 224 } 225 } 226 227 try { 228 boolean resolveOnlyBool = false; 229 if (resolveOnly != null) { 230 resolveOnlyBool = resolveOnly.getBoolean(ctx); 231 } 232 233 if (resolveOnlyBool) { 234 nextHandler.apply(ctx, parent); 235 } else { 236 applyWidgetHandler(ctx, parent, config, widgetInstance, value, true, nextHandler); 237 } 238 } finally { 239 ctx.setVariableMapper(orig); 240 } 241 } 242 } 243 244 public static void generateWidgetIdsRecursive(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget) { 245 generateWidgetId(ctx, helper, widget, true); 246 } 247 248 /** 249 * @since 7.2 250 */ 251 public static void generateWidgetId(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 252 boolean recursive) { 253 if (widget == null) { 254 return; 255 } 256 widget.setId(FaceletHandlerHelper.generateWidgetId(ctx, widget.getName())); 257 if (recursive) { 258 Widget[] subWidgets = widget.getSubWidgets(); 259 if (subWidgets != null) { 260 for (Widget subWidget : subWidgets) { 261 generateWidgetIdsRecursive(ctx, helper, subWidget); 262 } 263 } 264 } 265 } 266 267 public static void applyWidgetHandler(FaceletContext ctx, UIComponent parent, TagConfig config, Widget widget, 268 TagAttribute value, boolean fillVariables, FaceletHandler nextHandler) throws IOException { 269 if (widget == null) { 270 return; 271 } 272 273 FaceletHandlerHelper helper = new FaceletHandlerHelper(config); 274 275 TagConfig wtConfig = TagConfigFactory.createTagConfig(config, widget.getTagConfigId(), null, nextHandler); 276 WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); 277 WidgetTypeHandler handler = layoutService.getWidgetTypeHandler(wtConfig, widget); 278 279 if (handler == null) { 280 String widgetTypeName = widget.getType(); 281 String widgetTypeCategory = widget.getTypeCategory(); 282 String message = String.format("No widget handler found for type '%s' in category '%s'", widgetTypeName, 283 widgetTypeCategory); 284 log.error(message); 285 FaceletHandler h = helper.getErrorComponentHandler(null, message); 286 h.apply(ctx, parent); 287 return; 288 } 289 290 FaceletHandler fh = handler; 291 if (FaceletHandlerHelper.isDevModeEnabled(ctx)) { 292 // decorate handler with dev handler 293 FaceletHandler devHandler = handler.getDevFaceletHandler(config, widget); 294 if (devHandler != null) { 295 // expose the widget variable to sub dev handler 296 String widgetTagConfigId = widget.getTagConfigId(); 297 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 298 ExpressionFactory eFactory = ctx.getExpressionFactory(); 299 ValueExpression widgetVe = eFactory.createValueExpression(widget, Widget.class); 300 variables.put(RenderVariables.widgetVariables.widget.name(), widgetVe); 301 List<String> blockedPatterns = new ArrayList<String>(); 302 blockedPatterns.add(RenderVariables.widgetVariables.widget.name() + "*"); 303 FaceletHandler devAliasHandler = helper.getAliasFaceletHandler(widgetTagConfigId, variables, 304 blockedPatterns, devHandler); 305 String refId = widget.getName(); 306 fh = new DevTagHandler(config, refId, handler, devAliasHandler); 307 } 308 } 309 310 if (fillVariables) { 311 // expose widget variables 312 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 313 314 ValueExpression valueExpr; 315 if (value == null) { 316 valueExpr = new ValueExpressionLiteral(null, Object.class); 317 } else { 318 valueExpr = value.getValueExpression(ctx, Object.class); 319 } 320 321 variables.put(RenderVariables.globalVariables.value.name(), valueExpr); 322 variables.put(RenderVariables.globalVariables.value.name() + "_" + widget.getLevel(), valueExpr); 323 324 FaceletHandler handlerWithVars = helper.getAliasFaceletHandler(widget.getTagConfigId(), variables, null, fh); 325 // apply 326 handlerWithVars.apply(ctx, parent); 327 328 } else { 329 // just apply 330 fh.apply(ctx, parent); 331 } 332 } 333 334 @Override 335 @SuppressWarnings("rawtypes") 336 protected MetaRuleset createMetaRuleset(Class type) { 337 return null; 338 } 339 340}