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: WebLayoutManagerImpl.java 28510 2008-01-06 10:21:44Z sfermigier $ 018 */ 019 020package org.nuxeo.ecm.platform.forms.layout.service; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import javax.el.ExpressionFactory; 032import javax.el.ValueExpression; 033import javax.el.VariableMapper; 034import javax.faces.view.facelets.ComponentHandler; 035import javax.faces.view.facelets.FaceletContext; 036import javax.faces.view.facelets.FaceletHandler; 037import javax.faces.view.facelets.TagConfig; 038 039import org.apache.commons.lang.StringUtils; 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes; 043import org.nuxeo.ecm.platform.forms.layout.api.BuiltinWidgetModes; 044import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 045import org.nuxeo.ecm.platform.forms.layout.api.Layout; 046import org.nuxeo.ecm.platform.forms.layout.api.LayoutDefinition; 047import org.nuxeo.ecm.platform.forms.layout.api.LayoutRow; 048import org.nuxeo.ecm.platform.forms.layout.api.LayoutRowDefinition; 049import org.nuxeo.ecm.platform.forms.layout.api.LayoutTypeConfiguration; 050import org.nuxeo.ecm.platform.forms.layout.api.LayoutTypeDefinition; 051import org.nuxeo.ecm.platform.forms.layout.api.Widget; 052import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; 053import org.nuxeo.ecm.platform.forms.layout.api.WidgetReference; 054import org.nuxeo.ecm.platform.forms.layout.api.WidgetType; 055import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 056import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 057import org.nuxeo.ecm.platform.forms.layout.api.converters.LayoutConversionContext; 058import org.nuxeo.ecm.platform.forms.layout.api.converters.LayoutDefinitionConverter; 059import org.nuxeo.ecm.platform.forms.layout.api.converters.WidgetDefinitionConverter; 060import org.nuxeo.ecm.platform.forms.layout.api.exceptions.LayoutException; 061import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException; 062import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutImpl; 063import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutRowComparator; 064import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutRowImpl; 065import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; 066import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetImpl; 067import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetReferenceImpl; 068import org.nuxeo.ecm.platform.forms.layout.core.service.AbstractLayoutManager; 069import org.nuxeo.ecm.platform.forms.layout.core.service.LayoutStoreImpl; 070import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutDescriptor; 071import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutTypeDescriptor; 072import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor; 073import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetTypeDescriptor; 074import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper; 075import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables; 076import org.nuxeo.ecm.platform.forms.layout.facelets.WidgetTypeHandler; 077import org.nuxeo.ecm.platform.forms.layout.facelets.dev.DevTagHandler; 078import org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler; 079import org.nuxeo.ecm.platform.forms.layout.functions.LayoutFunctions; 080import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 081import org.nuxeo.runtime.api.Framework; 082import org.nuxeo.runtime.model.ComponentInstance; 083import org.nuxeo.runtime.model.ComponentName; 084 085import com.sun.faces.facelets.el.VariableMapperWrapper; 086 087/** 088 * Layout service implementation. 089 * 090 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 091 */ 092public class WebLayoutManagerImpl extends AbstractLayoutManager implements WebLayoutManager { 093 094 public static final ComponentName NAME = new ComponentName(WebLayoutManagerImpl.class.getName()); 095 096 private static final Log log = LogFactory.getLog(WebLayoutManagerImpl.class); 097 098 private static final long serialVersionUID = 1L; 099 100 public static final String WIDGET_TYPES_EP_NAME = LayoutStoreImpl.WIDGET_TYPES_EP_NAME; 101 102 /** 103 * @since 6.0 104 */ 105 public static final String LAYOUT_TYPES_EP_NAME = LayoutStoreImpl.LAYOUT_TYPES_EP_NAME; 106 107 public static final String WIDGETS_EP_NAME = LayoutStoreImpl.WIDGETS_EP_NAME; 108 109 public static final String LAYOUTS_EP_NAME = LayoutStoreImpl.LAYOUTS_EP_NAME; 110 111 public static final String PROPS_REF_EP_NAME = "disabledPropertyRefs"; 112 113 protected DisabledPropertyRefRegistry disabledPropertyRefsReg; 114 115 // Runtime component API 116 117 public WebLayoutManagerImpl() { 118 super(); 119 disabledPropertyRefsReg = new DisabledPropertyRefRegistry(); 120 } 121 122 @Override 123 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 124 if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) { 125 registerWidgetType(((WidgetTypeDescriptor) contribution).getWidgetTypeDefinition()); 126 } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) { 127 registerLayoutType(((LayoutTypeDescriptor) contribution).getLayoutTypeDefinition()); 128 } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) { 129 registerLayout(((LayoutDescriptor) contribution).getLayoutDefinition()); 130 } else if (extensionPoint.equals(WIDGETS_EP_NAME)) { 131 registerWidget(((WidgetDescriptor) contribution).getWidgetDefinition()); 132 } else if (extensionPoint.equals(PROPS_REF_EP_NAME)) { 133 registerDisabledPropertyRef(((DisabledPropertyRefDescriptor) contribution)); 134 } else { 135 log.error(String.format("Unknown extension point '%s', can't register !", extensionPoint)); 136 } 137 } 138 139 @Override 140 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 141 if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) { 142 unregisterWidgetType(((WidgetTypeDescriptor) contribution).getWidgetTypeDefinition()); 143 } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) { 144 unregisterLayoutType(((LayoutTypeDescriptor) contribution).getLayoutTypeDefinition()); 145 } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) { 146 unregisterLayout(((LayoutDescriptor) contribution).getLayoutDefinition()); 147 } else if (extensionPoint.equals(WIDGETS_EP_NAME)) { 148 unregisterWidget(((WidgetDescriptor) contribution).getWidgetDefinition()); 149 } else if (extensionPoint.equals(PROPS_REF_EP_NAME)) { 150 unregisterDisabledPropertyRef(((DisabledPropertyRefDescriptor) contribution)); 151 } else { 152 log.error(String.format("Unknown extension point '%s', can't unregister !", extensionPoint)); 153 } 154 } 155 156 // specific API (depends on JSF impl) 157 158 @Override 159 public String getDefaultStoreCategory() { 160 return JSF_CATEGORY; 161 } 162 163 @Override 164 public WidgetTypeHandler getWidgetTypeHandler(String typeName) throws WidgetException { 165 return getWidgetTypeHandler(getDefaultStoreCategory(), typeName); 166 } 167 168 @Override 169 public WidgetTypeHandler getWidgetTypeHandler(String typeCategory, String typeName) throws WidgetException { 170 if (StringUtils.isBlank(typeCategory)) { 171 typeCategory = getDefaultStoreCategory(); 172 } 173 WidgetType type = getLayoutStore().getWidgetType(typeCategory, typeName); 174 if (type == null) { 175 return null; 176 } 177 WidgetTypeHandler handler; 178 Class<?> klass = type.getWidgetTypeClass(); 179 if (klass == null) { 180 // implicit handler is the "template" one 181 handler = new TemplateWidgetTypeHandler(); 182 } else { 183 try { 184 // Thread context loader is not working in isolated EARs 185 handler = (WidgetTypeHandler) klass.newInstance(); 186 } catch (ReflectiveOperationException e) { 187 log.error("Caught error when instanciating widget type handler", e); 188 return null; 189 } 190 } 191 // set properties 192 handler.setProperties(type.getProperties()); 193 return handler; 194 } 195 196 /** 197 * Evaluates an EL expression in given context. 198 * <p> 199 * If the expression resolves to an EL expression, evaluate it again this is useful when retrieving the expression 200 * from a configuration file. 201 * <p> 202 * If given context is null, do no try to evaluate it and return the expression itself. 203 * 204 * @param context the facelet context. 205 * @param expression the string expression. 206 */ 207 protected static Object evaluateExpression(FaceletContext context, String expression) { 208 if (expression == null) { 209 return null; 210 } 211 if (context == null) { 212 return expression; 213 } 214 Object value = ComponentTagUtils.resolveElExpression(context, expression); 215 if (value != null && value instanceof String) { 216 // evaluate a second time in case it's another EL expression 217 value = ComponentTagUtils.resolveElExpression(context, (String) value); 218 } 219 return value; 220 } 221 222 /** 223 * Evaluates an expression to a boolean value. 224 */ 225 protected static Boolean getBooleanValue(FaceletContext context, String expression) { 226 Object value = evaluateExpression(context, expression); 227 if (value instanceof Boolean) { 228 return (Boolean) value; 229 } else if (value == null || value instanceof String) { 230 return Boolean.valueOf((String) value); 231 } else { 232 log.error(String.format("Could not get boolean value for '%s'", value)); 233 return Boolean.FALSE; 234 } 235 } 236 237 /** 238 * Evaluates an expression to a string value. 239 */ 240 protected static String getStringValue(FaceletContext context, String expression) { 241 Object value = evaluateExpression(context, expression); 242 if (value == null || value instanceof String) { 243 return (String) value; 244 } else { 245 log.error(String.format("Could not get string value for '%s'", value)); 246 return null; 247 } 248 } 249 250 protected static String getModeFromLayoutMode(FaceletContext context, WidgetDefinition wDef, String layoutMode) { 251 String wMode = getStringValue(context, wDef.getMode(layoutMode)); 252 if (wMode == null) { 253 wMode = BuiltinModes.getWidgetModeFromLayoutMode(layoutMode); 254 } 255 return wMode; 256 } 257 258 @Override 259 public Widget getWidget(FaceletContext ctx, String widgetName, String widgetCategory, String layoutMode, 260 String valueName, String layoutName) { 261 WidgetReference widgetRef = new WidgetReferenceImpl(widgetCategory, widgetName); 262 WidgetDefinition wDef = lookupWidget(widgetRef); 263 return getWidget(ctx, null, null, layoutName, null, wDef, widgetCategory, layoutMode, valueName, 0); 264 } 265 266 @Override 267 public Widget getWidget(FaceletContext ctx, WidgetDefinition wDef, String layoutMode, String valueName, 268 String layoutName) { 269 return getWidget(ctx, null, null, layoutName, null, wDef, getDefaultStoreCategory(), layoutMode, valueName, 0); 270 } 271 272 @Override 273 public Widget getWidget(FaceletContext ctx, LayoutConversionContext lctx, String conversionCat, 274 WidgetDefinition widgetDef, String layoutMode, String valueName, String layoutName) { 275 return getWidget(ctx, null, null, layoutName, null, widgetDef, getDefaultStoreCategory(), layoutMode, 276 valueName, 0); 277 } 278 279 /** 280 * Computes a widget from a definition for a mode in a given context. 281 * <p> 282 * If the widget is configured not to be rendered in the given mode, returns null. 283 * <p> 284 * Sub widgets are also computed recursively. 285 */ 286 @SuppressWarnings("deprecation") 287 protected Widget getWidget(FaceletContext context, LayoutConversionContext lctx, String conversionCat, 288 String layoutName, LayoutDefinition layoutDef, WidgetDefinition widgetDefinition, String widgetCategory, 289 String layoutMode, String valueName, int level) { 290 if (widgetDefinition == null) { 291 return null; 292 } 293 WidgetDefinition wDef = widgetDefinition.clone(); 294 if (lctx != null && !StringUtils.isBlank(conversionCat)) { 295 List<WidgetDefinitionConverter> lcs = getLayoutStore().getWidgetConverters(conversionCat); 296 for (WidgetDefinitionConverter wc : lcs) { 297 wDef = wc.getWidgetDefinition(wDef, lctx); 298 } 299 } 300 VariableMapper orig = null; 301 // avoid variable mapper changes if context is null for tests 302 if (context != null) { 303 // expose widget mode so that it can be used in a mode el 304 // expression 305 orig = context.getVariableMapper(); 306 VariableMapper vm = new VariableMapperWrapper(orig); 307 context.setVariableMapper(vm); 308 ExpressionFactory eFactory = context.getExpressionFactory(); 309 ValueExpression modeVe = eFactory.createValueExpression(layoutMode, String.class); 310 vm.setVariable(RenderVariables.globalVariables.mode.name(), modeVe); 311 } 312 String wMode = getModeFromLayoutMode(context, wDef, layoutMode); 313 if (context != null) { 314 context.setVariableMapper(orig); 315 } 316 317 if (BuiltinWidgetModes.HIDDEN.equals(wMode)) { 318 return null; 319 } 320 List<Widget> subWidgets = new ArrayList<Widget>(); 321 WidgetDefinition[] swDefs = wDef.getSubWidgetDefinitions(); 322 if (swDefs != null) { 323 for (WidgetDefinition swDef : swDefs) { 324 Widget subWidget = getWidget(context, lctx, conversionCat, layoutName, layoutDef, swDef, 325 widgetCategory, wMode, valueName, level + 1); 326 if (subWidget != null) { 327 subWidgets.add(subWidget); 328 } 329 } 330 } 331 332 WidgetReference[] swRefs = wDef.getSubWidgetReferences(); 333 if (swRefs != null) { 334 for (WidgetReference swRef : swRefs) { 335 String cat = swRef.getCategory(); 336 if (StringUtils.isBlank(cat)) { 337 cat = widgetCategory; 338 } 339 WidgetDefinition swDef = lookupWidget(layoutDef, new WidgetReferenceImpl(cat, swRef.getName())); 340 if (swDef == null) { 341 log.error(String.format("Widget '%s' not found in layout %s", swRef.getName(), layoutName)); 342 } else { 343 Widget subWidget = getWidget(context, lctx, conversionCat, layoutName, layoutDef, swDef, cat, 344 wMode, valueName, level + 1); 345 if (subWidget != null) { 346 subWidgets.add(subWidget); 347 } 348 } 349 } 350 } 351 352 boolean required = getBooleanValue(context, wDef.getRequired(layoutMode, wMode)).booleanValue(); 353 354 String wType = wDef.getType(); 355 String wTypeCat = wDef.getTypeCategory(); 356 // fill default property and control values from the widget definition 357 Map<String, Serializable> props = new HashMap<String, Serializable>(); 358 Map<String, Serializable> controls = new HashMap<String, Serializable>(); 359 String actualWTypeCat = getStoreCategory(wTypeCat); 360 WidgetTypeDefinition def = getLayoutStore().getWidgetTypeDefinition(actualWTypeCat, wType); 361 362 WidgetTypeConfiguration conf = def != null ? def.getConfiguration() : null; 363 if (conf != null) { 364 Map<String, Serializable> defaultProps = conf.getDefaultPropertyValues(wMode); 365 if (defaultProps != null && !defaultProps.isEmpty()) { 366 props.putAll(defaultProps); 367 } 368 Map<String, Serializable> defaultControls = conf.getDefaultControlValues(wMode); 369 if (defaultControls != null && !defaultControls.isEmpty()) { 370 controls.putAll(defaultControls); 371 } 372 } 373 374 props.putAll(wDef.getProperties(layoutMode, wMode)); 375 controls.putAll(wDef.getControls(layoutMode, wMode)); 376 377 WidgetImpl widget = new WidgetImpl(layoutName, wDef.getName(), wMode, wType, valueName, 378 wDef.getFieldDefinitions(), wDef.getLabel(layoutMode), wDef.getHelpLabel(layoutMode), 379 wDef.isTranslated(), wDef.isHandlingLabels(), props, required, subWidgets.toArray(new Widget[0]), 380 level, wDef.getSelectOptions(), LayoutFunctions.computeWidgetDefinitionId(wDef), 381 wDef.getRenderingInfos(layoutMode)); 382 widget.setControls(controls); 383 widget.setTypeCategory(actualWTypeCat); 384 if (Framework.isDevModeSet()) { 385 widget.setDefinition(wDef); 386 } 387 return widget; 388 } 389 390 @Override 391 public Layout getLayout(FaceletContext ctx, String layoutName, String mode, String valueName) 392 throws LayoutException { 393 return getLayout(ctx, layoutName, mode, valueName, null, false); 394 } 395 396 @Override 397 public Layout getLayout(FaceletContext ctx, String layoutName, String mode, String valueName, 398 List<String> selectedRows, boolean selectAllRowsByDefault) { 399 return getLayout(ctx, layoutName, null, mode, valueName, selectedRows, selectAllRowsByDefault); 400 } 401 402 @Override 403 public Layout getLayout(FaceletContext ctx, String layoutName, String layoutCategory, String mode, 404 String valueName, List<String> selectedRows, boolean selectAllRowsByDefault) { 405 if (StringUtils.isBlank(layoutCategory)) { 406 layoutCategory = getDefaultStoreCategory(); 407 } 408 LayoutDefinition layoutDef = getLayoutStore().getLayoutDefinition(layoutCategory, layoutName); 409 if (layoutDef == null) { 410 log.debug(String.format("Layout '%s' not found for category '%s'", layoutName, layoutCategory)); 411 return null; 412 } 413 return getLayout(ctx, layoutDef, mode, valueName, selectedRows, selectAllRowsByDefault); 414 } 415 416 @Override 417 public Layout getLayout(FaceletContext ctx, LayoutDefinition layoutDef, String mode, String valueName, 418 List<String> selectedRows, boolean selectAllRowsByDefault) { 419 return getLayout(ctx, null, null, layoutDef, mode, valueName, selectedRows, selectAllRowsByDefault); 420 } 421 422 @Override 423 public Layout getLayout(FaceletContext ctx, LayoutConversionContext lctx, String conversionCat, 424 LayoutDefinition layoutDefinition, String mode, String valueName, List<String> selectedRows, 425 boolean selectAllRowsByDefault) { 426 if (layoutDefinition == null) { 427 log.debug("Layout definition is null"); 428 return null; 429 } 430 if (ctx == null) { 431 log.warn("Layout creation computed in a null facelet context: expressions " 432 + "found in the layout definition will not be evaluated"); 433 } 434 LayoutDefinition lDef = layoutDefinition.clone(); 435 if (lctx != null && !StringUtils.isBlank(conversionCat)) { 436 List<LayoutDefinitionConverter> lcs = getLayoutStore().getLayoutConverters(conversionCat); 437 for (LayoutDefinitionConverter lc : lcs) { 438 lDef = lc.getLayoutDefinition(lDef, lctx); 439 } 440 } 441 String layoutName = lDef.getName(); 442 LayoutRowDefinition[] rowsDef = lDef.getRows(); 443 List<LayoutRow> rows = new ArrayList<LayoutRow>(); 444 Set<String> foundRowNames = new HashSet<String>(); 445 int rowIndex = -1; 446 for (LayoutRowDefinition rowDef : rowsDef) { 447 rowIndex++; 448 String rowName = rowDef.getName(); 449 if (rowName == null) { 450 rowName = rowDef.getDefaultName(rowIndex); 451 if (selectedRows != null) { 452 log.debug(String.format("Generating default name '%s' in " 453 + "layout '%s' for row or column at index %s", rowName, layoutName, 454 Integer.valueOf(rowIndex))); 455 } 456 } 457 boolean emptyRow = true; 458 if (selectedRows != null && !selectedRows.contains(rowName) && !rowDef.isAlwaysSelected()) { 459 continue; 460 } 461 if (selectedRows == null && !selectAllRowsByDefault && !rowDef.isSelectedByDefault() 462 && !rowDef.isAlwaysSelected()) { 463 continue; 464 } 465 List<Widget> widgets = new ArrayList<Widget>(); 466 for (WidgetReference widgetRef : rowDef.getWidgetReferences()) { 467 String widgetName = widgetRef.getName(); 468 if (widgetName == null || widgetName.length() == 0) { 469 // no widget at this place 470 widgets.add(null); 471 continue; 472 } 473 String cat = widgetRef.getCategory(); 474 if (StringUtils.isBlank(cat)) { 475 cat = getDefaultStoreCategory(); 476 } 477 WidgetDefinition wDef = lookupWidget(lDef, new WidgetReferenceImpl(cat, widgetName)); 478 if (wDef == null) { 479 log.error(String.format("Widget '%s' not found in layout %s", widgetName, layoutName)); 480 widgets.add(null); 481 continue; 482 } 483 Widget widget = getWidget(ctx, lctx, conversionCat, layoutName, lDef, wDef, cat, mode, valueName, 0); 484 if (widget != null) { 485 emptyRow = false; 486 } 487 widgets.add(widget); 488 } 489 if (!emptyRow) { 490 rows.add(new LayoutRowImpl(rowName, rowDef.isSelectedByDefault(), rowDef.isAlwaysSelected(), widgets, 491 rowDef.getProperties(mode), LayoutFunctions.computeLayoutRowDefinitionId(rowDef))); 492 } 493 foundRowNames.add(rowName); 494 } 495 if (selectedRows != null) { 496 Collections.sort(rows, new LayoutRowComparator(selectedRows)); 497 for (String selectedRow : selectedRows) { 498 if (!foundRowNames.contains(selectedRow)) { 499 log.debug(String.format("Selected row or column named '%s' " + "was not found in layout '%s'", 500 selectedRow, layoutName)); 501 } 502 } 503 } 504 505 String layoutTypeCategory = lDef.getTypeCategory(); 506 String actualLayoutTypeCategory = getStoreCategory(layoutTypeCategory); 507 LayoutTypeDefinition layoutTypeDef = null; 508 String layoutType = lDef.getType(); 509 if (!StringUtils.isBlank(layoutType)) { 510 // retrieve type for templates and props mapping 511 layoutTypeDef = getLayoutStore().getLayoutTypeDefinition(actualLayoutTypeCategory, layoutType); 512 if (layoutTypeDef == null) { 513 log.debug(String.format("Layout type '%s' not found for category '%s'", layoutType, layoutTypeCategory)); 514 } 515 } 516 517 String template = lDef.getTemplate(mode); 518 Map<String, Serializable> props = new HashMap<>(); 519 if (layoutTypeDef != null) { 520 if (StringUtils.isEmpty(template)) { 521 template = layoutTypeDef.getTemplate(mode); 522 } 523 LayoutTypeConfiguration conf = layoutTypeDef.getConfiguration(); 524 if (conf != null) { 525 Map<String, Serializable> typeProps = conf.getDefaultPropertyValues(mode); 526 if (typeProps != null) { 527 props.putAll(typeProps); 528 } 529 } 530 } 531 Map<String, Serializable> lprops = lDef.getProperties(mode); 532 if (lprops != null) { 533 props.putAll(lprops); 534 } 535 LayoutImpl layout = new LayoutImpl(lDef.getName(), mode, template, rows, lDef.getColumns(), props, 536 LayoutFunctions.computeLayoutDefinitionId(lDef)); 537 layout.setValueName(valueName); 538 layout.setType(layoutType); 539 layout.setTypeCategory(actualLayoutTypeCategory); 540 if (Framework.isDevModeSet()) { 541 layout.setDefinition(lDef); 542 // resolve template in "dev" mode, avoiding default lookup on "any" 543 // mode 544 Map<String, String> templates = lDef.getTemplates(); 545 String devTemplate = templates != null ? templates.get(BuiltinModes.DEV) : null; 546 if (layoutTypeDef != null && StringUtils.isEmpty(devTemplate)) { 547 Map<String, String> typeTemplates = layoutTypeDef.getTemplates(); 548 devTemplate = typeTemplates != null ? typeTemplates.get(BuiltinModes.DEV) : null; 549 } 550 layout.setDevTemplate(devTemplate); 551 } 552 return layout; 553 } 554 555 @Override 556 public FaceletHandler getFaceletHandler(FaceletContext ctx, TagConfig config, Widget widget) { 557 return getFaceletHandler(ctx, config, widget, null); 558 } 559 560 @Override 561 public FaceletHandler getFaceletHandler(FaceletContext ctx, TagConfig config, Widget widget, 562 FaceletHandler nextHandler) { 563 String widgetTypeName = widget.getType(); 564 String widgetTypeCategory = widget.getTypeCategory(); 565 WidgetTypeHandler handler = getWidgetTypeHandler(widgetTypeCategory, widgetTypeName); 566 if (handler == null) { 567 FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, config); 568 String message = String.format("No widget handler found for type '%s' in category '%s'", widgetTypeName, 569 widgetTypeCategory); 570 log.error(message); 571 ComponentHandler output = helper.getErrorComponentHandler(null, message); 572 return output; 573 } else { 574 FaceletHandler[] subHandlers = null; 575 List<FaceletHandler> subHandlersList = new ArrayList<FaceletHandler>(); 576 if (nextHandler != null) { 577 subHandlersList.add(nextHandler); 578 } 579 if (!subHandlersList.isEmpty()) { 580 subHandlers = subHandlersList.toArray(new FaceletHandler[0]); 581 } 582 FaceletHandler widgetHandler = handler.getFaceletHandler(ctx, config, widget, subHandlers); 583 584 if (FaceletHandlerHelper.isDevModeEnabled(ctx)) { 585 // decorate handler with dev handler 586 FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, config); 587 FaceletHandler devHandler = handler.getDevFaceletHandler(ctx, config, widget); 588 if (devHandler == null) { 589 return widgetHandler; 590 } 591 // expose the widget variable to sub dev handler 592 String widgetTagConfigId = widget.getTagConfigId(); 593 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 594 ExpressionFactory eFactory = ctx.getExpressionFactory(); 595 ValueExpression widgetVe = eFactory.createValueExpression(widget, Widget.class); 596 variables.put(RenderVariables.widgetVariables.widget.name(), widgetVe); 597 List<String> blockedPatterns = new ArrayList<String>(); 598 blockedPatterns.add(RenderVariables.widgetVariables.widget.name() + "*"); 599 FaceletHandler devAliasHandler = helper.getAliasTagHandler(widgetTagConfigId, variables, 600 blockedPatterns, devHandler); 601 String refId = widget.getName(); 602 FaceletHandler widgetDevHandler = new DevTagHandler(config, refId, widgetHandler, devAliasHandler); 603 return widgetDevHandler; 604 } 605 return widgetHandler; 606 } 607 } 608 609 @Override 610 public Widget createWidget(FaceletContext ctx, String type, String mode, String valueName, 611 Map<String, Serializable> properties, Widget[] subWidgets) { 612 return createWidget(ctx, type, mode, valueName, null, null, null, null, properties, subWidgets); 613 } 614 615 @Override 616 public Widget createWidget(FaceletContext ctx, String type, String mode, String valueName, 617 List<FieldDefinition> fieldDefinitions, String label, String helpLabel, Boolean translated, 618 Map<String, Serializable> properties, Widget[] subWidgets) { 619 return createWidget( 620 ctx, 621 createWidgetDefinition(ctx, type, null, mode, valueName, fieldDefinitions, null, label, helpLabel, 622 translated, properties, subWidgets), mode, valueName, subWidgets); 623 } 624 625 @Override 626 public Widget createWidget(FaceletContext ctx, WidgetDefinition wDef, String mode, String valueName, 627 Widget[] subWidgets) { 628 629 String wType = wDef.getType(); 630 String wTypeCat = wDef.getTypeCategory(); 631 // fill default property and control values from the widget definition 632 Map<String, Serializable> props = new HashMap<String, Serializable>(); 633 Map<String, Serializable> controls = new HashMap<String, Serializable>(); 634 String actualWTypeCat = getStoreCategory(wTypeCat); 635 WidgetTypeDefinition def = getLayoutStore().getWidgetTypeDefinition(actualWTypeCat, wType); 636 637 boolean required = false; 638 WidgetTypeConfiguration conf = def != null ? def.getConfiguration() : null; 639 if (conf != null) { 640 Map<String, Serializable> defaultProps = conf.getDefaultPropertyValues(mode); 641 if (defaultProps != null && !defaultProps.isEmpty()) { 642 props.putAll(defaultProps); 643 } 644 Map<String, Serializable> defaultControls = conf.getDefaultControlValues(mode); 645 if (defaultControls != null && !defaultControls.isEmpty()) { 646 controls.putAll(defaultControls); 647 } 648 } 649 Map<String, Serializable> modeProps = wDef.getProperties(mode, mode); 650 if (modeProps != null) { 651 props.putAll(modeProps); 652 Serializable requiredProp = props.get(WidgetDefinition.REQUIRED_PROPERTY_NAME); 653 if (requiredProp != null) { 654 if (requiredProp instanceof Boolean) { 655 required = ((Boolean) requiredProp).booleanValue(); 656 } else if (requiredProp instanceof String) { 657 required = getBooleanValue(ctx, (String) requiredProp).booleanValue(); 658 } else { 659 log.error(String.format("Invalid property '%s' on widget: '%s'", 660 WidgetDefinition.REQUIRED_PROPERTY_NAME, requiredProp)); 661 } 662 } 663 } 664 Map<String, Serializable> modeControls = wDef.getControls(mode, mode); 665 if (modeControls != null) { 666 controls.putAll(modeControls); 667 } 668 WidgetImpl widget = new WidgetImpl("layout", wDef.getName(), mode, wType, valueName, 669 wDef.getFieldDefinitions(), wDef.getLabel(mode), wDef.getHelpLabel(mode), wDef.isTranslated(), props, 670 required, subWidgets, 0, null, LayoutFunctions.computeWidgetDefinitionId(wDef)); 671 widget.setControls(controls); 672 widget.setTypeCategory(actualWTypeCat); 673 widget.setDynamic(wDef.isDynamic()); 674 widget.setGlobal(wDef.isGlobal()); 675 if (Framework.isDevModeSet()) { 676 widget.setDefinition(wDef); 677 } 678 return widget; 679 } 680 681 protected WidgetDefinition createWidgetDefinition(FaceletContext ctx, String type, String category, String mode, 682 String valueName, List<FieldDefinition> fieldDefinitions, String widgetName, String label, 683 String helpLabel, Boolean translated, Map<String, Serializable> properties, Widget[] subWidgets) { 684 String wName = widgetName; 685 if (StringUtils.isBlank(widgetName)) { 686 wName = type; 687 } 688 WidgetDefinitionImpl wDef = new WidgetDefinitionImpl(wName, type, label, helpLabel, 689 Boolean.TRUE.equals(translated), null, fieldDefinitions, properties, null); 690 wDef.setDynamic(true); 691 return wDef; 692 } 693 694 /** 695 * @since 5.6 696 */ 697 protected void registerDisabledPropertyRef(DisabledPropertyRefDescriptor desc) { 698 disabledPropertyRefsReg.addContribution(desc); 699 log.info(String.format("Registered disabled property reference descriptor: %s", desc.toString())); 700 } 701 702 /** 703 * @since 5.6 704 */ 705 protected void unregisterDisabledPropertyRef(DisabledPropertyRefDescriptor desc) { 706 disabledPropertyRefsReg.removeContribution(desc); 707 log.info(String.format("Removed disabled property reference descriptor: %s", desc.toString())); 708 } 709 710 @Override 711 public boolean referencePropertyAsExpression(String name, Serializable value, String widgetType, String widgetMode, 712 String template) { 713 return referencePropertyAsExpression(name, value, widgetType, null, widgetMode, template); 714 } 715 716 @Override 717 public boolean referencePropertyAsExpression(String name, Serializable value, String widgetType, 718 String widgetTypeCategory, String widgetMode, String template) { 719 if ((value instanceof String) && (ComponentTagUtils.isValueReference((String) value))) { 720 return false; 721 } 722 String cat = widgetTypeCategory; 723 if (widgetTypeCategory == null) { 724 cat = WebLayoutManager.JSF_CATEGORY; 725 } 726 for (DisabledPropertyRefDescriptor desc : disabledPropertyRefsReg.getDisabledPropertyRefs()) { 727 if (Boolean.TRUE.equals(desc.getEnabled()) && desc.matches(name, widgetType, cat, widgetMode, template)) { 728 return false; 729 } 730 } 731 return true; 732 } 733 734}