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