/* * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * * $Id: LayoutTagHandler.java 30553 2008-02-24 15:51:31Z atchertchian $ */ package org.nuxeo.ecm.platform.forms.layout.facelets; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.el.ELException; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.view.facelets.ComponentConfig; import javax.faces.view.facelets.ComponentHandler; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.FaceletHandler; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagAttributes; import javax.faces.view.facelets.TagConfig; import javax.faces.view.facelets.TagException; import javax.faces.view.facelets.TagHandler; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.forms.layout.api.Layout; import org.nuxeo.ecm.platform.forms.layout.api.LayoutDefinition; import org.nuxeo.ecm.platform.forms.layout.api.Widget; import org.nuxeo.ecm.platform.forms.layout.facelets.dev.DevTagHandler; import org.nuxeo.ecm.platform.forms.layout.facelets.dev.LayoutDevTagHandler; import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; import org.nuxeo.ecm.platform.ui.web.binding.BlockingVariableMapper; import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory; import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; import org.nuxeo.ecm.platform.ui.web.util.FaceletDebugTracer; import org.nuxeo.runtime.api.Framework; import com.sun.faces.facelets.el.VariableMapperWrapper; import com.sun.faces.facelets.tag.TagAttributeImpl; import com.sun.faces.facelets.tag.TagAttributesImpl; import com.sun.faces.facelets.tag.ui.ComponentRef; import com.sun.faces.facelets.tag.ui.ComponentRefHandler; import com.sun.faces.facelets.tag.ui.DecorateHandler; /** * Layout tag handler. * <p> * Computes a layout in given facelet context, for given mode and value attributes. The layout can either be computed * from a layout definition, or by a layout name, where the layout service will lookup the corresponding definition. * <p> * If a template is found for this layout, include the corresponding facelet and use facelet template features to * iterate over rows and widgets. * <p> * Since 5.6, the layout name attribute also accepts a comma separated list of layout names. * * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> */ public class LayoutTagHandler extends TagHandler { private static final Log log = LogFactory.getLog(LayoutTagHandler.class); protected final TagConfig config; /** * The layout instance to render, instead of resolving it from a name or definition * * @since 5.7 */ protected final TagAttribute layout; protected final TagAttribute name; /** * @since 5.5. */ protected final TagAttribute category; /** * @since 5.4.2 */ protected final TagAttribute definition; protected final TagAttribute mode; protected final TagAttribute value; protected final TagAttribute template; protected final TagAttribute selectedRows; protected final TagAttribute selectedColumns; protected final TagAttribute selectAllByDefault; /** * Parameter used to specify that layout should not be rendered, only resolved and exposed to the context. * * @since 5.7 */ protected final TagAttribute resolveOnly; protected final TagAttribute[] vars; protected final String[] reservedVarsArray = { "id", "layout", "name", "category", "definition", "mode", "value", "template", "selectedRows", "selectedColumns", "selectAllByDefault", "resolveOnly" }; public LayoutTagHandler(TagConfig config) { super(config); this.config = config; name = getAttribute("name"); category = getAttribute("category"); definition = getAttribute("definition"); layout = getAttribute("layout"); if (name == null && definition == null && layout == null) { throw new TagException(this.tag, "At least one of attributes 'name', 'layout' or 'definition' is required"); } mode = getAttribute("mode"); value = getRequiredAttribute("value"); if (layout == null && (name != null || definition != null)) { if (mode == null) { throw new TagException(this.tag, "Attribute 'mode' is required when using attribute" + " 'name' or 'definition' so that the layout instance can be resolved"); } } template = getAttribute("template"); selectedRows = getAttribute("selectedRows"); selectedColumns = getAttribute("selectedColumns"); if (selectedRows != null && selectedColumns != null) { throw new TagException(this.tag, "Attributes 'selectedRows' " + "and 'selectedColumns' are aliases: only one of them should be filled"); } selectAllByDefault = getAttribute("selectAllByDefault"); resolveOnly = getAttribute("resolveOnly"); vars = tag.getAttributes().getAll(); } @SuppressWarnings("unchecked") // TODO: add javadoc about variables exposed public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { if (!FaceletHandlerHelper.isAliasOptimEnabled()) { applyCompat(ctx, parent); return; } long start = FaceletDebugTracer.start(); String logId = null; try { WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); // add additional properties put on tag Map<String, Serializable> additionalProps = new HashMap<String, Serializable>(); List<String> reservedVars = Arrays.asList(reservedVarsArray); for (TagAttribute var : vars) { String localName = var.getLocalName(); if (!reservedVars.contains(localName)) { // resolve value as there's no alias value expression exposed // for layout properties additionalProps.put(localName, (Serializable) var.getObject(ctx)); } } VariableMapper orig = ctx.getVariableMapper(); try { // expose some layout variables before layout creation so that they // can be used in mode expressions BlockingVariableMapper vm = new BlockingVariableMapper(orig); ctx.setVariableMapper(vm); FaceletHandlerHelper helper = new FaceletHandlerHelper(config); ExpressionFactory eFactory = ctx.getExpressionFactory(); Layout layoutInstance = null; String valueName = value.getValue(); if (ComponentTagUtils.isStrictValueReference(valueName)) { valueName = ComponentTagUtils.getBareValueName(valueName); } String templateValue = null; if (template != null) { templateValue = template.getValue(ctx); } boolean resolveOnlyValue = false; if (resolveOnly != null) { resolveOnlyValue = resolveOnly.getBoolean(ctx); } if (layout != null) { // resolve layout instance given as attribute layoutInstance = (Layout) layout.getObject(ctx, Layout.class); if (layoutInstance == null) { String errMsg = "Layout instance not found"; applyErrorHandler(ctx, parent, helper, errMsg); } else { fillVariablesForLayoutBuild(ctx, eFactory, vm, layoutInstance.getMode()); layoutInstance.setValueName(valueName); applyLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vm, resolveOnlyValue); } } else { // build layout instance from other attributes String modeValue = mode.getValue(ctx); List<String> selectedRowsValue = null; boolean selectAllByDefaultValue = false; fillVariablesForLayoutBuild(ctx, eFactory, vm, modeValue); if (selectedRows != null || selectedColumns != null) { if (selectedRows != null) { selectedRowsValue = (List<String>) selectedRows.getObject(ctx, List.class); } else if (selectedColumns != null) { List<String> selectedColumnsList = (List<String>) selectedColumns.getObject(ctx, List.class); // Handle empty selected columns list as null to // display all columns. if (selectedColumnsList != null && selectedColumnsList.isEmpty()) { selectedColumnsList = null; } selectedRowsValue = selectedColumnsList; } } if (selectAllByDefault != null) { selectAllByDefaultValue = selectAllByDefault.getBoolean(ctx); } if (name != null) { String layoutCategory = null; if (category != null) { layoutCategory = category.getValue(ctx); } String nameValue = name.getValue(ctx); List<String> layoutNames = resolveLayoutNames(nameValue); logId = layoutNames.toString(); for (String layoutName : layoutNames) { layoutInstance = layoutService.getLayout(ctx, layoutName, layoutCategory, modeValue, valueName, selectedRowsValue, selectAllByDefaultValue); if (layoutInstance == null) { String errMsg = "Layout '" + layoutName + "' not found"; applyErrorHandler(ctx, parent, helper, errMsg); } else { applyLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vm, resolveOnlyValue); } } } if (definition != null) { LayoutDefinition layoutDef = (LayoutDefinition) definition.getObject(ctx, LayoutDefinition.class); if (layoutDef == null) { String errMsg = "Layout definition resolved to null"; applyErrorHandler(ctx, parent, helper, errMsg); } else { layoutInstance = layoutService.getLayout(ctx, layoutDef, modeValue, valueName, selectedRowsValue, selectAllByDefaultValue); applyLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vm, resolveOnlyValue); if (layoutInstance != null) { logId = layoutInstance.getId(); } else { logId = layoutDef.getName() + " (def)"; } } } } } finally { // layout resolved => cleanup variable mapper ctx.setVariableMapper(orig); } } finally { FaceletDebugTracer.trace(start, config.getTag(), logId); } } /** * Resolves layouts names, splitting on character "," and trimming resulting names, and allowing empty strings if * the whole string is not empty to ease up rendering of layout names using variables. * <p> * For instance, if value is null or empty, will return a single empty layout name "". If value is "," it will * return an empty list, triggering no error for usage like <nxl:layout name="#{myLayout}, #{myOtherLayout}" [...] * /> */ protected List<String> resolveLayoutNames(String nameValue) { List<String> res = new ArrayList<String>(); if (nameValue != null) { String[] split = nameValue.split(",|\\s"); if (split != null) { for (String item : split) { if (!StringUtils.isBlank(item)) { res.add(item.trim()); } } } } return res; } protected void applyLayoutHandler(FaceletContext ctx, UIComponent parent, FaceletHandlerHelper helper, WebLayoutManager layoutService, Layout layoutInstance, String templateValue, Map<String, Serializable> additionalProps, BlockingVariableMapper vm, boolean resolveOnly) throws IOException, FacesException, ELException { // set unique id on layout, unless layout is only resolved if (!resolveOnly) { layoutInstance.setId(FaceletHandlerHelper.generateLayoutId(ctx, layoutInstance.getName())); } // add additional properties put on tag Map<String, Serializable> layoutProps = layoutInstance.getProperties(); if (additionalProps != null && !additionalProps.isEmpty()) { for (Map.Entry<String, Serializable> entry : additionalProps.entrySet()) { // XXX: do not override with empty property values if already // set on the layout properties String key = entry.getKey(); Serializable value = entry.getValue(); if (layoutProps.containsKey(key) && (value == null || ((value instanceof String) && StringUtils.isBlank((String) value)))) { // do not override property on layout if (log.isDebugEnabled()) { log.debug(String.format( "Do not override property '%s' with " + "empty value on layout named '%s'", key, layoutInstance.getName())); } } else { layoutInstance.setProperty(key, value); } } } if (StringUtils.isBlank(templateValue)) { templateValue = layoutInstance.getTemplate(); } if (!resolveOnly) { boolean scaffold = Boolean.parseBoolean(String.valueOf(layoutInstance.getProperty("scaffold"))); if (scaffold) { // generate ids on widgets Map<String, Widget> widgetMap = layoutInstance.getWidgetMap(); if (widgetMap != null) { for (Widget widget : widgetMap.values()) { if (widget != null && (widget.getId() == null)) { WidgetTagHandler.generateWidgetId(ctx, helper, widget, false); } } } } } // expose rendering variables fillVariablesForLayoutRendering(ctx, ctx.getExpressionFactory(), layoutService, vm, layoutInstance); final String layoutTagConfigId = layoutInstance.getTagConfigId(); if (resolveOnly) { nextHandler.apply(ctx, parent); } else { if (!StringUtils.isBlank(templateValue)) { TagAttribute srcAttr = helper.createAttribute("template", templateValue); TagConfig config = TagConfigFactory.createTagConfig(this.config, layoutTagConfigId, FaceletHandlerHelper.getTagAttributes(srcAttr), nextHandler); FaceletHandler templateHandler = new DecorateHandler(config); // NXP-18639: always wrap next include handler in a component ref for tagConfigId to be taken into // account and anchored in the view with this id. ComponentConfig ref = TagConfigFactory.createComponentConfig(this.config, layoutTagConfigId, new TagAttributesImpl(new TagAttributeImpl[] {}), templateHandler, ComponentRef.COMPONENT_TYPE, null); FaceletHandler includeHandler = new ComponentRefHandler(ref); if (FaceletHandlerHelper.isDevModeEnabled(ctx)) { // decorate handler with dev handler FaceletHandler devHandler = getDevFaceletHandler(ctx, helper, config, layoutInstance); FaceletHandler nextHandler; if (devHandler == null) { nextHandler = includeHandler; } else { nextHandler = new DevTagHandler(config, layoutInstance.getName(), includeHandler, devHandler); } nextHandler.apply(ctx, parent); } else { includeHandler.apply(ctx, parent); } } else { String errMsg = "Missing template property for layout '" + layoutInstance.getName() + "'"; applyErrorHandler(ctx, parent, helper, errMsg); } } } protected void fillVariablesForLayoutBuild(FaceletContext ctx, ExpressionFactory eFactory, BlockingVariableMapper vm, String modeValue) { ValueExpression valueExpr = value.getValueExpression(ctx, Object.class); vm.setVariable(RenderVariables.globalVariables.value.name(), valueExpr); vm.setVariable(RenderVariables.globalVariables.layoutValue.name(), valueExpr); ValueExpression modeVe = eFactory.createValueExpression(modeValue, String.class); vm.setVariable(RenderVariables.globalVariables.layoutMode.name(), modeVe); // mode as alias to layoutMode vm.setVariable(RenderVariables.globalVariables.mode.name(), modeVe); } /** * Computes variables for rendering, making available the layout instance and its properties to the context. */ protected void fillVariablesForLayoutRendering(FaceletContext ctx, ExpressionFactory eFactory, WebLayoutManager layoutService, BlockingVariableMapper vm, Layout layoutInstance) { // expose layout value ValueExpression layoutVe = eFactory.createValueExpression(layoutInstance, Layout.class); vm.setVariable(RenderVariables.layoutVariables.layout.name(), layoutVe); vm.addBlockedPattern(RenderVariables.layoutVariables.layout.name()); // expose layout properties too for (Map.Entry<String, Serializable> prop : layoutInstance.getProperties().entrySet()) { String key = prop.getKey(); String name = RenderVariables.layoutVariables.layoutProperty.name() + "_" + key; vm.setVariable(name, eFactory.createValueExpression(prop.getValue(), Object.class)); } vm.addBlockedPattern(RenderVariables.layoutVariables.layoutProperty.name() + "_*"); // expose layout row count for row variables reference Integer rowCount = null; if (layoutInstance.getRows() != null) { rowCount = layoutInstance.getRows().length; } vm.setVariable(RenderVariables.layoutVariables.layoutRowCount.name(), eFactory.createValueExpression(rowCount, Integer.class)); vm.addBlockedPattern(RenderVariables.layoutVariables.layoutRowCount.name()); } protected void applyErrorHandler(FaceletContext ctx, UIComponent parent, FaceletHandlerHelper helper, String message) throws IOException { log.error(message); ComponentHandler output = helper.getErrorComponentHandler(null, message); output.apply(ctx, parent); } protected FaceletHandler getDevFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, TagConfig config, Layout layout) { if (StringUtils.isBlank(layout.getDevTemplate())) { return null; } // use the default dev handler for widget types TagAttribute attr = helper.createAttribute("layout", "#{" + RenderVariables.layoutVariables.layout.name() + "}"); TagAttributes devWidgetAttributes = FaceletHandlerHelper.getTagAttributes(attr); TagConfig devWidgetConfig = TagConfigFactory.createTagConfig(config, layout.getTagConfigId(), devWidgetAttributes, new org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler()); return new LayoutDevTagHandler(devWidgetConfig); } /** * Compatibility methods */ @SuppressWarnings("unchecked") public void applyCompat(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { long start = FaceletDebugTracer.start(); String logId = null; try { WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); // add additional properties put on tag Map<String, Serializable> additionalProps = new HashMap<String, Serializable>(); List<String> reservedVars = Arrays.asList(reservedVarsArray); for (TagAttribute var : vars) { String localName = var.getLocalName(); if (!reservedVars.contains(localName)) { // resolve value as there's no alias value expression exposed // for layout properties additionalProps.put(localName, (Serializable) var.getObject(ctx)); } } // expose some layout variables before layout creation so that they // can be used in mode expressions VariableMapper orig = ctx.getVariableMapper(); FaceletHandlerHelper helper = new FaceletHandlerHelper(config); try { VariableMapper vm = new VariableMapperWrapper(orig); ctx.setVariableMapper(vm); Layout layoutInstance = null; String valueName = value.getValue(); if (ComponentTagUtils.isStrictValueReference(valueName)) { valueName = ComponentTagUtils.getBareValueName(valueName); } String templateValue = null; if (template != null) { templateValue = template.getValue(ctx); } boolean resolveOnlyValue = false; if (resolveOnly != null) { resolveOnlyValue = resolveOnly.getBoolean(ctx); } if (layout != null) { // resolve layout instance given as attribute layoutInstance = (Layout) layout.getObject(ctx, Layout.class); if (layoutInstance == null) { String errMsg = "Layout instance not found"; applyErrorHandler(ctx, parent, helper, errMsg); } else { Map<String, ValueExpression> vars = getVariablesForLayoutBuild(ctx, layoutInstance.getMode()); for (Map.Entry<String, ValueExpression> var : vars.entrySet()) { vm.setVariable(var.getKey(), var.getValue()); } layoutInstance.setValueName(valueName); applyCompatLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vars, resolveOnlyValue); } } else { // build layout instance from other attributes String modeValue = mode.getValue(ctx); List<String> selectedRowsValue = null; boolean selectAllByDefaultValue = false; Map<String, ValueExpression> vars = getVariablesForLayoutBuild(ctx, modeValue); for (Map.Entry<String, ValueExpression> var : vars.entrySet()) { vm.setVariable(var.getKey(), var.getValue()); } if (selectedRows != null || selectedColumns != null) { if (selectedRows != null) { selectedRowsValue = (List<String>) selectedRows.getObject(ctx, List.class); } else if (selectedColumns != null) { List<String> selectedColumnsList = (List<String>) selectedColumns.getObject(ctx, List.class); // Handle empty selected columns list as null to // display all columns. if (selectedColumnsList != null && selectedColumnsList.isEmpty()) { selectedColumnsList = null; } selectedRowsValue = selectedColumnsList; } } if (selectAllByDefault != null) { selectAllByDefaultValue = selectAllByDefault.getBoolean(ctx); } if (name != null) { String layoutCategory = null; if (category != null) { layoutCategory = category.getValue(ctx); } String nameValue = name.getValue(ctx); List<String> layoutNames = resolveLayoutNames(nameValue); logId = layoutNames.toString(); for (String layoutName : layoutNames) { layoutInstance = layoutService.getLayout(ctx, layoutName, layoutCategory, modeValue, valueName, selectedRowsValue, selectAllByDefaultValue); if (layoutInstance == null) { String errMsg = "Layout '" + layoutName + "' not found"; applyErrorHandler(ctx, parent, helper, errMsg); } else { applyCompatLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vars, resolveOnlyValue); } } } if (definition != null) { LayoutDefinition layoutDef = (LayoutDefinition) definition.getObject(ctx, LayoutDefinition.class); if (layoutDef == null) { String errMsg = "Layout definition resolved to null"; applyErrorHandler(ctx, parent, helper, errMsg); } else { layoutInstance = layoutService.getLayout(ctx, layoutDef, modeValue, valueName, selectedRowsValue, selectAllByDefaultValue); applyCompatLayoutHandler(ctx, parent, helper, layoutService, layoutInstance, templateValue, additionalProps, vars, resolveOnlyValue); if (layoutInstance != null) { logId = layoutInstance.getId(); } else { logId = layoutDef.getName() + " (def)"; } } } } } finally { // layout resolved => cleanup variable mapper ctx.setVariableMapper(orig); } } finally { FaceletDebugTracer.trace(start, config.getTag(), logId); } } protected void applyCompatLayoutHandler(FaceletContext ctx, UIComponent parent, FaceletHandlerHelper helper, WebLayoutManager layoutService, Layout layoutInstance, String templateValue, Map<String, Serializable> additionalProps, Map<String, ValueExpression> vars, boolean resolveOnly) throws IOException, FacesException, ELException { // set unique id on layout, unless layout is only resolved if (!resolveOnly) { layoutInstance.setId(FaceletHandlerHelper.generateLayoutId(ctx, layoutInstance.getName())); } // add additional properties put on tag Map<String, Serializable> layoutProps = layoutInstance.getProperties(); if (additionalProps != null && !additionalProps.isEmpty()) { for (Map.Entry<String, Serializable> entry : additionalProps.entrySet()) { // XXX: do not override with empty property values if already // set on the layout properties String key = entry.getKey(); Serializable value = entry.getValue(); if (layoutProps.containsKey(key) && (value == null || ((value instanceof String) && StringUtils.isBlank((String) value)))) { // do not override property on layout if (log.isDebugEnabled()) { log.debug(String.format( "Do not override property '%s' with " + "empty value on layout named '%s'", key, layoutInstance.getName())); } } else { layoutInstance.setProperty(key, value); } } } if (StringUtils.isBlank(templateValue)) { templateValue = layoutInstance.getTemplate(); } if (!resolveOnly) { boolean scaffold = Boolean.parseBoolean(String.valueOf(layoutInstance.getProperty("scaffold"))); if (scaffold) { // generate ids on widgets Map<String, Widget> widgetMap = layoutInstance.getWidgetMap(); if (widgetMap != null) { for (Widget widget : widgetMap.values()) { if (widget != null && (widget.getId() == null)) { WidgetTagHandler.generateWidgetId(ctx, helper, widget, false); } } } } } // expose layout instance to variable mapper to ensure good // resolution of properties ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression layoutVe = eFactory.createValueExpression(layoutInstance, Layout.class); ctx.getVariableMapper().setVariable(RenderVariables.layoutVariables.layout.name(), layoutVe); // expose all variables through an alias tag handler vars.putAll(getVariablesForLayoutRendering(ctx, layoutService, layoutInstance)); List<String> blockedPatterns = new ArrayList<String>(); blockedPatterns.add(RenderVariables.layoutVariables.layout.name()); blockedPatterns.add(RenderVariables.layoutVariables.layoutProperty.name() + "_*"); final String layoutTagConfigId = layoutInstance.getTagConfigId(); if (resolveOnly) { FaceletHandler handler = helper.getAliasFaceletHandler(layoutTagConfigId, vars, blockedPatterns, nextHandler); // apply handler.apply(ctx, parent); } else { if (!StringUtils.isBlank(templateValue)) { TagAttribute srcAttr = helper.createAttribute("template", templateValue); TagConfig config = TagConfigFactory.createTagConfig(this.config, layoutTagConfigId, FaceletHandlerHelper.getTagAttributes(srcAttr), nextHandler); FaceletHandler includeHandler = new DecorateHandler(config); FaceletHandler handler; if (FaceletHandlerHelper.isDevModeEnabled(ctx)) { // decorate handler with dev handler FaceletHandler devHandler = getDevFaceletHandler(ctx, helper, config, layoutInstance); FaceletHandler nextHandler; if (devHandler == null) { nextHandler = includeHandler; } else { nextHandler = new DevTagHandler(config, layoutInstance.getName(), includeHandler, devHandler); } handler = helper.getAliasFaceletHandler(layoutTagConfigId, vars, blockedPatterns, nextHandler); } else { handler = helper.getAliasFaceletHandler(layoutTagConfigId, vars, blockedPatterns, includeHandler); } // apply handler.apply(ctx, parent); } else { String errMsg = "Missing template property for layout '" + layoutInstance.getName() + "'"; applyErrorHandler(ctx, parent, helper, errMsg); } } } protected Map<String, ValueExpression> getVariablesForLayoutBuild(FaceletContext ctx, String modeValue) { Map<String, ValueExpression> vars = new HashMap<String, ValueExpression>(); ValueExpression valueExpr = value.getValueExpression(ctx, Object.class); vars.put(RenderVariables.globalVariables.value.name(), valueExpr); // vars.put(RenderVariables.globalVariables.document.name(), // valueExpr); vars.put(RenderVariables.globalVariables.layoutValue.name(), valueExpr); ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression modeVe = eFactory.createValueExpression(modeValue, String.class); vars.put(RenderVariables.globalVariables.layoutMode.name(), modeVe); // mode as alias to layoutMode vars.put(RenderVariables.globalVariables.mode.name(), modeVe); return vars; } /** * Computes variables for rendering, making available the layout instance and its properties to the context. */ protected Map<String, ValueExpression> getVariablesForLayoutRendering(FaceletContext ctx, WebLayoutManager layoutService, Layout layoutInstance) { Map<String, ValueExpression> vars = new HashMap<String, ValueExpression>(); ExpressionFactory eFactory = ctx.getExpressionFactory(); // expose layout value ValueExpression layoutVe = eFactory.createValueExpression(layoutInstance, Layout.class); vars.put(RenderVariables.layoutVariables.layout.name(), layoutVe); // expose layout properties too for (Map.Entry<String, Serializable> prop : layoutInstance.getProperties().entrySet()) { String key = prop.getKey(); String name = RenderVariables.layoutVariables.layoutProperty.name() + "_" + key; String value; Serializable valueInstance = prop.getValue(); if (!layoutService.referencePropertyAsExpression(key, valueInstance, null, null, null, null)) { // FIXME: this will not be updated correctly using ajax value = (String) valueInstance; } else { // create a reference so that it's a real expression and it's // not kept (cached) in a component value on ajax refresh value = "#{" + RenderVariables.layoutVariables.layout.name() + ".properties." + key + "}"; } vars.put(name, eFactory.createValueExpression(ctx, value, Object.class)); } return vars; } /** * End of compatibility methods */ }