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: TemplateWidgetTypeHandler.java 28244 2007-12-18 19:44:57Z atchertchian $
020 */
021
022package org.nuxeo.ecm.platform.forms.layout.facelets.plugins;
023
024import java.io.IOException;
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import javax.el.ExpressionFactory;
032import javax.el.ValueExpression;
033import javax.faces.component.UIComponent;
034import javax.faces.view.facelets.FaceletContext;
035import javax.faces.view.facelets.TagAttribute;
036import javax.faces.view.facelets.TagAttributes;
037import javax.faces.view.facelets.TagConfig;
038import javax.faces.view.facelets.TagHandler;
039
040import org.apache.commons.logging.Log;
041import org.apache.commons.logging.LogFactory;
042import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition;
043import org.nuxeo.ecm.platform.forms.layout.api.Widget;
044import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException;
045import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper;
046import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables;
047import org.nuxeo.ecm.platform.forms.layout.facelets.ValueExpressionHelper;
048import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager;
049import org.nuxeo.ecm.platform.ui.web.binding.MapValueExpression;
050import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory;
051import org.nuxeo.runtime.api.Framework;
052
053import com.sun.faces.facelets.tag.ui.DecorateHandler;
054
055/**
056 * Template widget type.
057 *
058 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
059 */
060public class TemplateWidgetTypeHandler extends AbstractWidgetTypeHandler {
061
062    private static final Log log = LogFactory.getLog(TemplateWidgetTypeHandler.class);
063
064    public static final String TEMPLATE_PROPERTY_NAME = "template";
065
066    /**
067     * Property that can be put on the widget type definition to decide whether the widget type should bind to parent
068     * value when no field is set
069     *
070     * @since 5.6
071     */
072    public static final String BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME = "bindValueIfNoField";
073
074    public TemplateWidgetTypeHandler(TagConfig config) {
075        super(config);
076    }
077
078    @Override
079    public void apply(FaceletContext ctx, UIComponent parent, Widget widget) throws WidgetException, IOException {
080        String template = getTemplateValue(widget);
081        if (template == null) {
082            log.error("Missing template property for widget " + widget.getName() + " in layout "
083                    + widget.getLayoutName());
084            return;
085        }
086        FaceletHandlerHelper helper = new FaceletHandlerHelper(tagConfig);
087        String widgetId = widget.getId();
088        TagAttributes attributes = helper.getTagAttributes(widgetId, widget);
089        TagAttribute templateAttr = getTemplateAttribute(helper);
090        if (templateAttr == null) {
091            templateAttr = helper.createAttribute(TEMPLATE_PROPERTY_NAME, template);
092        }
093        attributes = FaceletHandlerHelper.addTagAttribute(attributes, templateAttr);
094        String widgetTagConfigId = widget.getTagConfigId();
095        TagConfig config = TagConfigFactory.createTagConfig(tagConfig, widgetTagConfigId, attributes, nextHandler);
096
097        Map<String, ValueExpression> variables = getVariablesForRendering(ctx, helper, widget, widgetTagConfigId,
098                template);
099
100        List<String> blockedPatterns = new ArrayList<String>();
101        blockedPatterns.add(RenderVariables.widgetVariables.field.name() + "*");
102        blockedPatterns.add(RenderVariables.widgetVariables.fieldOrValue.name());
103        blockedPatterns.add(RenderVariables.widgetVariables.widgetProperty.name() + "_*");
104        blockedPatterns.add(RenderVariables.widgetVariables.widgetProperties.name());
105        blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name() + "_*");
106
107        DecorateHandler includeHandler = new DecorateHandler(config);
108        TagHandler handler = helper.getAliasTagHandler(widgetTagConfigId, variables, blockedPatterns, includeHandler);
109        handler.apply(ctx, parent);
110    }
111
112    /**
113     * Computes variables for rendering, making available the field values in templates using the format "field_0",
114     * "field_1", etc. and also the widget properties using the format "widgetProperty_thePropertyName".
115     */
116    protected Map<String, ValueExpression> getVariablesForRendering(FaceletContext ctx, FaceletHandlerHelper helper,
117            Widget widget, String widgetTagConfigId, String template) {
118        Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>();
119        ExpressionFactory eFactory = ctx.getExpressionFactory();
120
121        FieldDefinition[] fieldDefs = widget.getFieldDefinitions();
122        // expose field variables
123        FieldDefinition firstField = null;
124        if (fieldDefs != null && fieldDefs.length > 0) {
125            for (int i = 0; i < fieldDefs.length; i++) {
126                if (i == 0) {
127                    addFieldVariable(variables, ctx, widget, fieldDefs[i], null);
128                    firstField = fieldDefs[i];
129                }
130                addFieldVariable(variables, ctx, widget, fieldDefs[i], Integer.valueOf(i));
131            }
132        } else if (getBindValueIfNoFieldValue(widget)) {
133            // expose value as first parameter
134            addFieldVariable(variables, ctx, widget, null, null);
135            addFieldVariable(variables, ctx, widget, null, Integer.valueOf(0));
136        }
137
138        // add binding "fieldOrValue" available since 5.6, in case template
139        // widget is always supposed to bind value when no field is defined
140        String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), firstField);
141        variables.put(RenderVariables.widgetVariables.fieldOrValue.name(),
142                eFactory.createValueExpression(ctx, computedValue, Object.class));
143
144        // expose widget properties too
145        WebLayoutManager layoutService = Framework.getService(WebLayoutManager.class);
146        Map<String, ValueExpression> mappedExpressions = new HashMap<String, ValueExpression>();
147        for (Map.Entry<String, Serializable> prop : widget.getProperties().entrySet()) {
148            String key = prop.getKey();
149            String name = RenderVariables.widgetVariables.widgetProperty.name() + "_" + key;
150            String value;
151            Serializable valueInstance = prop.getValue();
152            if (!layoutService.referencePropertyAsExpression(key, valueInstance, widget.getType(),
153                    widget.getTypeCategory(), widget.getMode(), template)) {
154                // FIXME: this will not be updated correctly using ajax
155                value = (String) valueInstance;
156            } else {
157                // create a reference so that it's a real expression and it's
158                // not kept (cached) in a component value on ajax refresh
159                value = "#{" + RenderVariables.widgetVariables.widget.name() + ".properties." + key + "}";
160            }
161            ValueExpression ve = eFactory.createValueExpression(ctx, value, Object.class);
162            variables.put(name, ve);
163            mappedExpressions.put(key, ve);
164        }
165        variables.put(RenderVariables.widgetVariables.widgetProperties.name(),
166                new MapValueExpression(mappedExpressions));
167        // expose widget controls too
168        for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) {
169            String key = ctrl.getKey();
170            String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key;
171            String value = "#{" + RenderVariables.widgetVariables.widget.name() + ".controls." + key + "}";
172            variables.put(name, eFactory.createValueExpression(ctx, value, Object.class));
173        }
174        return variables;
175    }
176
177    protected void addFieldVariable(Map<String, ValueExpression> variables, FaceletContext ctx, Widget widget,
178            FieldDefinition fieldDef, Integer index) {
179        String computedName;
180        if (index == null) {
181            computedName = RenderVariables.widgetVariables.field.name();
182        } else {
183            computedName = RenderVariables.widgetVariables.field.name() + "_" + index;
184        }
185        String computedValue = ValueExpressionHelper.createExpressionString(widget.getValueName(), fieldDef);
186
187        ExpressionFactory eFactory = ctx.getExpressionFactory();
188        variables.put(computedName, eFactory.createValueExpression(ctx, computedValue, Object.class));
189    }
190
191    /**
192     * Returns the "template" property value, looking up on the widget definition definition first, and on the widget
193     * type definition if not found.
194     */
195    protected String getTemplateValue(Widget widget) {
196        return lookupProperty(TEMPLATE_PROPERTY_NAME, widget);
197    }
198
199    /**
200     * Helper method to retrieve a property value, looking up on the widget definition first, and on the widget type
201     * definition if not found.
202     *
203     * @since 7.2
204     */
205    protected String lookupProperty(String name, Widget widget) {
206        // lookup in the widget configuration
207        String val = (String) widget.getProperty(name);
208        if (val == null) {
209            // lookup in the widget type configuration
210            val = getProperty(name);
211        }
212        return val;
213    }
214
215    /**
216     * Returns the "bindValueIfNoField" property value, looking up on the widget type definition first, and on the
217     * widget definition if not found.
218     *
219     * @since 5.6
220     * @param widget
221     * @return
222     */
223    protected boolean getBindValueIfNoFieldValue(Widget widget) {
224        Object value = getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME);
225        if (value == null) {
226            value = widget.getProperty(BIND_VALUE_IF_NO_FIELD_PROPERTY_NAME);
227        }
228        if (value == null) {
229            return false;
230        }
231        if (value instanceof Boolean) {
232            return Boolean.TRUE.equals(value);
233        }
234        return Boolean.TRUE.equals(Boolean.valueOf(value.toString()));
235
236    }
237
238    /**
239     * Returns the template attribute.
240     */
241    protected TagAttribute getTemplateAttribute(FaceletHandlerHelper helper) {
242        // do not return anything as it will be computed from the widget
243        // properties anyway.
244        return null;
245    }
246
247}