/* * (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: WidgetTagHandler.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.FaceletContext; import javax.faces.view.facelets.FaceletHandler; import javax.faces.view.facelets.MetaRuleset; import javax.faces.view.facelets.MetaTagHandler; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagConfig; import javax.faces.view.facelets.TagException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.el.ValueExpressionLiteral; import org.nuxeo.ecm.platform.forms.layout.api.Widget; import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; import org.nuxeo.ecm.platform.forms.layout.facelets.dev.DevTagHandler; 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; /** * Widget tag handler. * <p> * Applies {@link WidgetTypeHandler} found for given widget, in given mode and for given value. * * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> */ public class WidgetTagHandler extends MetaTagHandler { private static final Log log = LogFactory.getLog(WidgetTagHandler.class); protected final TagConfig config; protected final TagAttribute widget; /** * @since 5.6 */ protected final TagAttribute name; /** * @since 5.6 */ protected final TagAttribute category; /** * @since 5.6 */ protected final TagAttribute definition; /** * @since 5.6 */ protected final TagAttribute mode; /** * @since 5.6 */ protected final TagAttribute layoutName; /** * @since 5.7 */ protected final TagAttribute resolveOnly; protected final TagAttribute value; protected final TagAttribute[] vars; protected final String[] reservedVarsArray = { "id", "widget", "name", "category", "definition", "mode", "layoutName", "value", "resolveOnly" }; public WidgetTagHandler(TagConfig config) { super(config); this.config = config; widget = getAttribute("widget"); name = getAttribute("name"); definition = getAttribute("definition"); category = getAttribute("category"); mode = getAttribute("mode"); layoutName = getAttribute("layoutName"); resolveOnly = getAttribute("resolveOnly"); value = getAttribute("value"); vars = tag.getAttributes().getAll(); // additional checks if (name == null && widget == null && definition == null) { throw new TagException(this.tag, "At least one of attributes 'name', 'widget' " + "or 'definition' is required"); } if (widget == 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 " + "widget instance " + "can be resolved"); } } } /** * Renders given widget resolving its {@link FaceletHandler} from {@link WebLayoutManager} configuration. * <p> * Variables exposed: {@link RenderVariables.globalVariables#value}, same variable suffixed with "_n" where n is the * widget level, and {@link RenderVariables.globalVariables#document}. */ public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { long start = FaceletDebugTracer.start(); Widget widgetInstance = null; try { // compute value name to set on widget instance in case it's changed // from first computation String valueName = null; if (value != null) { valueName = value.getValue(); } if (ComponentTagUtils.isStrictValueReference(valueName)) { valueName = ComponentTagUtils.getBareValueName(valueName); } // build handler boolean widgetInstanceBuilt = false; if (widget != null) { widgetInstance = (Widget) widget.getObject(ctx, Widget.class); if (widgetInstance != null && valueName != null) { widgetInstance.setValueName(valueName); } } else { // resolve widget according to name and mode (and optional // category) WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); String modeValue = mode.getValue(ctx); String layoutNameValue = null; if (layoutName != null) { layoutNameValue = layoutName.getValue(ctx); } if (name != null) { String nameValue = name.getValue(ctx); String catValue = null; if (category != null) { catValue = category.getValue(ctx); } widgetInstance = layoutService.getWidget(ctx, nameValue, catValue, modeValue, valueName, layoutNameValue); widgetInstanceBuilt = true; } else if (definition != null) { WidgetDefinition widgetDef = (WidgetDefinition) definition.getObject(ctx, WidgetDefinition.class); if (widgetDef != null) { widgetInstance = layoutService.getWidget(ctx, widgetDef, modeValue, valueName, layoutNameValue); widgetInstanceBuilt = true; } } } if (widgetInstance != null) { // add additional properties put on tag String widgetPropertyMarker = RenderVariables.widgetVariables.widgetProperty.name() + "_"; List<String> reservedVars = Arrays.asList(reservedVarsArray); for (TagAttribute var : vars) { String localName = var.getLocalName(); if (!reservedVars.contains(localName)) { if (localName != null && localName.startsWith(widgetPropertyMarker)) { localName = localName.substring(widgetPropertyMarker.length()); } widgetInstance.setProperty(localName, var.getValue()); } } VariableMapper orig = ctx.getVariableMapper(); try { if (FaceletHandlerHelper.isAliasOptimEnabled()) { applyOptimized(ctx, orig, widgetInstance, widgetInstanceBuilt); } else { applyCompat(ctx, orig, widgetInstance, widgetInstanceBuilt); } boolean resolveOnlyBool = false; if (resolveOnly != null) { resolveOnlyBool = resolveOnly.getBoolean(ctx); } if (resolveOnlyBool) { nextHandler.apply(ctx, parent); } else { applyWidgetHandler(ctx, parent, config, widgetInstance, value, true, nextHandler); } } finally { ctx.setVariableMapper(orig); } } } finally { FaceletDebugTracer.trace(start, config.getTag(), widgetInstance == null ? null : widgetInstance.getId()); } } public static void generateWidgetIdsRecursive(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget) { generateWidgetId(ctx, helper, widget, true); } /** * @since 7.2 */ public static void generateWidgetId(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, boolean recursive) { if (widget == null) { return; } widget.setId(FaceletHandlerHelper.generateWidgetId(ctx, widget.getName())); if (recursive) { Widget[] subWidgets = widget.getSubWidgets(); if (subWidgets != null) { for (Widget subWidget : subWidgets) { generateWidgetIdsRecursive(ctx, helper, subWidget); } } } } protected void applyOptimized(FaceletContext ctx, VariableMapper orig, Widget widgetInstance, boolean widgetInstanceBuilt) { BlockingVariableMapper vm = new BlockingVariableMapper(orig); ctx.setVariableMapper(vm); if (widgetInstanceBuilt) { // expose widget variable to the context as layout row has not done it already, and set unique id on // widget before exposing it to the context FaceletHandlerHelper helper = new FaceletHandlerHelper(config); WidgetTagHandler.generateWidgetId(ctx, helper, widgetInstance, false); exposeWidgetVariables(ctx, vm, widgetInstance, null, false); } } protected void applyCompat(FaceletContext ctx, VariableMapper orig, Widget widgetInstance, boolean widgetInstanceBuilt) { if (!widgetInstanceBuilt) { return; } // expose widget variable to the context as layout row has not done it already, and set unique id on // widget before exposing it to the context FaceletHandlerHelper helper = new FaceletHandlerHelper(config); WidgetTagHandler.generateWidgetId(ctx, helper, widgetInstance, false); VariableMapper vm = new VariableMapperWrapper(orig); ctx.setVariableMapper(vm); ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression widgetVe = eFactory.createValueExpression(widgetInstance, Widget.class); vm.setVariable(RenderVariables.widgetVariables.widget.name(), widgetVe); // expose widget controls too for (Map.Entry<String, Serializable> ctrl : widgetInstance.getControls().entrySet()) { String key = ctrl.getKey(); String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key; String value = "#{" + RenderVariables.widgetVariables.widget.name() + ".controls." + key + "}"; vm.setVariable(name, eFactory.createValueExpression(ctx, value, Object.class)); } } public static void applyWidgetHandler(FaceletContext ctx, UIComponent parent, TagConfig config, Widget widget, TagAttribute value, boolean fillVariables, FaceletHandler nextHandler) throws IOException { if (widget == null) { return; } FaceletHandlerHelper helper = new FaceletHandlerHelper(config); TagConfig wtConfig = TagConfigFactory.createTagConfig(config, widget.getTagConfigId(), null, nextHandler); WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class); WidgetTypeHandler handler = layoutService.getWidgetTypeHandler(wtConfig, widget); if (handler == null) { String widgetTypeName = widget.getType(); String widgetTypeCategory = widget.getTypeCategory(); String message = String.format("No widget handler found for type '%s' in category '%s'", widgetTypeName, widgetTypeCategory); log.error(message); FaceletHandler h = helper.getErrorComponentHandler(null, message); h.apply(ctx, parent); return; } FaceletHandler fh = handler; if (FaceletHandlerHelper.isDevModeEnabled(ctx)) { // decorate handler with dev handler FaceletHandler devHandler = handler.getDevFaceletHandler(config, widget); if (devHandler != null) { // expose the widget variable to sub dev handler String widgetTagConfigId = widget.getTagConfigId(); Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression widgetVe = eFactory.createValueExpression(widget, Widget.class); variables.put(RenderVariables.widgetVariables.widget.name(), widgetVe); List<String> blockedPatterns = new ArrayList<String>(); blockedPatterns.add(RenderVariables.widgetVariables.widget.name() + "*"); FaceletHandler devAliasHandler = helper.getAliasFaceletHandler(widgetTagConfigId, variables, blockedPatterns, devHandler); String refId = widget.getName(); fh = new DevTagHandler(config, refId, handler, devAliasHandler); } } if (FaceletHandlerHelper.isAliasOptimEnabled()) { if (fillVariables) { // expose widget variables VariableMapper cvm = ctx.getVariableMapper(); if (!(cvm instanceof BlockingVariableMapper)) { throw new IllegalArgumentException( "Current context variable mapper should be an instance of MetaVariableMapper"); } BlockingVariableMapper vm = (BlockingVariableMapper) cvm; ValueExpression valueExpr; if (value == null) { valueExpr = new ValueExpressionLiteral(null, Object.class); } else { valueExpr = value.getValueExpression(ctx, Object.class); } vm.setVariable(RenderVariables.globalVariables.value.name(), valueExpr); vm.setVariable(RenderVariables.globalVariables.value.name() + "_" + widget.getLevel(), valueExpr); } fh.apply(ctx, parent); } else { if (fillVariables) { // expose widget variables Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); ValueExpression valueExpr; if (value == null) { valueExpr = new ValueExpressionLiteral(null, Object.class); } else { valueExpr = value.getValueExpression(ctx, Object.class); } variables.put(RenderVariables.globalVariables.value.name(), valueExpr); variables.put(RenderVariables.globalVariables.value.name() + "_" + widget.getLevel(), valueExpr); FaceletHandler handlerWithVars = helper.getAliasFaceletHandler(widget.getTagConfigId(), variables, null, fh); // apply handlerWithVars.apply(ctx, parent); } else { // just apply fh.apply(ctx, parent); } } } public static void exposeWidgetVariables(FaceletContext ctx, BlockingVariableMapper vm, Widget widget, Integer widgetIndex, boolean exposeLevel) { ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression widgetVe = eFactory.createValueExpression(widget, Widget.class); vm.setVariable(RenderVariables.widgetVariables.widget.name(), widgetVe); vm.addBlockedPattern(RenderVariables.widgetVariables.widget.name()); ValueExpression widgetIndexVe = null; if (widgetIndex != null) { widgetIndexVe = eFactory.createValueExpression(widgetIndex, Integer.class); vm.setVariable(RenderVariables.widgetVariables.widgetIndex.name(), widgetIndexVe); } if (exposeLevel && !FaceletHandlerHelper.isAliasOptimEnabled()) { Integer level = null; if (widget != null) { level = widget.getLevel(); } vm.setVariable(RenderVariables.widgetVariables.widget.name() + "_" + level, widgetVe); if (widgetIndexVe != null) { vm.setVariable(RenderVariables.widgetVariables.widgetIndex.name() + "_" + level, widgetIndexVe); } vm.addBlockedPattern(RenderVariables.widgetVariables.widget.name() + "_*"); vm.addBlockedPattern(RenderVariables.widgetVariables.widgetIndex.name() + "*"); } // expose widget controls too if (widget != null) { for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) { String key = ctrl.getKey(); String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key; ValueExpression ve = eFactory.createValueExpression(ctrl.getValue(), Object.class); vm.setVariable(name, ve); } } vm.addBlockedPattern(RenderVariables.widgetVariables.widgetControl.name() + "_*"); } @Override @SuppressWarnings("rawtypes") protected MetaRuleset createMetaRuleset(Class type) { return null; } }