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