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: LayoutRowWidgetTagHandler.java 30553 2008-02-24 15:51:31Z atchertchian $
020 */
021
022package org.nuxeo.ecm.platform.forms.layout.facelets;
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.ELException;
032import javax.el.ExpressionFactory;
033import javax.el.ValueExpression;
034import javax.el.VariableMapper;
035import javax.faces.FacesException;
036import javax.faces.component.UIComponent;
037import javax.faces.view.facelets.FaceletContext;
038import javax.faces.view.facelets.FaceletException;
039import javax.faces.view.facelets.FaceletHandler;
040import javax.faces.view.facelets.TagAttribute;
041import javax.faces.view.facelets.TagConfig;
042import javax.faces.view.facelets.TagHandler;
043
044import org.apache.commons.logging.Log;
045import org.apache.commons.logging.LogFactory;
046import org.nuxeo.ecm.platform.forms.layout.api.LayoutRow;
047import org.nuxeo.ecm.platform.forms.layout.api.Widget;
048import org.nuxeo.ecm.platform.ui.web.binding.BlockingVariableMapper;
049
050/**
051 * Layout widget recursion tag handler.
052 * <p>
053 * Iterates over a layout row widgets and apply next handlers as many times as needed.
054 * <p>
055 * Only works when used inside a tag using the {@link LayoutRowTagHandler}.
056 *
057 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
058 */
059public class LayoutRowWidgetTagHandler extends TagHandler {
060
061    private static final Log log = LogFactory.getLog(LayoutRowWidgetTagHandler.class);
062
063    protected final TagConfig config;
064
065    /**
066     * @since 7.2
067     */
068    protected final TagAttribute recomputeIds;
069
070    public LayoutRowWidgetTagHandler(TagConfig config) {
071        super(config);
072        this.config = config;
073        recomputeIds = getAttribute("recomputeIds");
074    }
075
076    /**
077     * For each widget in current row, exposes widget variables and applies next handler.
078     * <p>
079     * Needs row to be exposed in context, so works in conjunction with {@link LayoutRowTagHandler}.
080     * <p>
081     * Widget variables exposed: {@link RenderVariables.widgetVariables#widget} , same variable suffixed with "_n" where
082     * n is the widget level, and {@link RenderVariables.widgetVariables#widgetIndex}.
083     */
084    public void apply(FaceletContext ctx, UIComponent parent)
085            throws IOException, FacesException, FaceletException, ELException {
086        if (FaceletHandlerHelper.isAliasOptimEnabled()) {
087            applyOptimized(ctx, parent);
088        } else {
089            applyCompat(ctx, parent);
090        }
091    }
092
093    protected void applyOptimized(FaceletContext ctx, UIComponent parent)
094            throws IOException, FacesException, FaceletException, ELException {
095        // resolve widgets from row in context
096        LayoutRow row = null;
097        String rowVariableName = getInstanceName();
098        FaceletHandlerHelper helper = new FaceletHandlerHelper(config);
099        TagAttribute rowAttribute = helper.createAttribute(rowVariableName, "#{" + rowVariableName + "}");
100        if (rowAttribute != null) {
101            row = (LayoutRow) rowAttribute.getObject(ctx, LayoutRow.class);
102        }
103        if (row == null) {
104            log.error("Could not resolve layout row " + rowAttribute);
105            return;
106        }
107
108        Widget[] widgets = row.getWidgets();
109        if (widgets == null || widgets.length == 0) {
110            return;
111        }
112
113        boolean recomputeIdsBool = false;
114        if (recomputeIds != null) {
115            recomputeIdsBool = recomputeIds.getBoolean(ctx);
116        }
117
118        VariableMapper orig = ctx.getVariableMapper();
119        try {
120            int widgetCounter = 0;
121            for (Widget widget : widgets) {
122                BlockingVariableMapper vm = new BlockingVariableMapper(orig);
123                ctx.setVariableMapper(vm);
124
125                // set unique id on widget before exposing it to the context, but assumes iteration could be done
126                // several times => do not generate id again if already set, unless specified by attribute
127                // "recomputeIds"
128                if (widget != null && (widget.getId() == null || recomputeIdsBool)) {
129                    WidgetTagHandler.generateWidgetId(ctx, helper, widget, false);
130                }
131
132                WidgetTagHandler.exposeWidgetVariables(ctx, vm, widget, widgetCounter, true);
133
134                nextHandler.apply(ctx, parent);
135                widgetCounter++;
136            }
137        } finally {
138            ctx.setVariableMapper(orig);
139        }
140    }
141
142    protected String getInstanceName() {
143        return RenderVariables.rowVariables.layoutRow.name();
144    }
145
146    protected void applyCompat(FaceletContext ctx, UIComponent parent)
147            throws IOException, FacesException, FaceletException, ELException {
148        // resolve widgets from row in context
149        LayoutRow row = null;
150        String rowVariableName = RenderVariables.rowVariables.layoutRow.name();
151        FaceletHandlerHelper helper = new FaceletHandlerHelper(config);
152        TagAttribute rowAttribute = helper.createAttribute(rowVariableName, "#{" + rowVariableName + "}");
153        if (rowAttribute != null) {
154            row = (LayoutRow) rowAttribute.getObject(ctx, LayoutRow.class);
155        }
156        if (row == null) {
157            log.error("Could not resolve layout row " + rowAttribute);
158            return;
159        }
160
161        Widget[] widgets = row.getWidgets();
162        if (widgets == null || widgets.length == 0) {
163            return;
164        }
165
166        boolean recomputeIdsBool = false;
167        if (recomputeIds != null) {
168            recomputeIdsBool = recomputeIds.getBoolean(ctx);
169        }
170
171        int widgetCounter = 0;
172        for (Widget widget : widgets) {
173            // set unique id on widget before exposing it to the context, but assumes iteration could be done several
174            // times => do not generate id again if already set, unless specified by attribute "recomputeIds"
175            if (widget != null && (widget.getId() == null || recomputeIdsBool)) {
176                WidgetTagHandler.generateWidgetId(ctx, helper, widget, false);
177            }
178
179            // expose widget variables
180            Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>();
181            ExpressionFactory eFactory = ctx.getExpressionFactory();
182            ValueExpression widgetVe = eFactory.createValueExpression(widget, Widget.class);
183            variables.put(RenderVariables.widgetVariables.widget.name(), widgetVe);
184            Integer level = null;
185            String tagConfigId = null;
186            if (widget != null) {
187                level = Integer.valueOf(widget.getLevel());
188                tagConfigId = widget.getTagConfigId();
189            }
190            variables.put(RenderVariables.widgetVariables.widget.name() + "_" + level, widgetVe);
191            ValueExpression widgetIndexVe = eFactory.createValueExpression(Integer.valueOf(widgetCounter),
192                    Integer.class);
193            variables.put(RenderVariables.widgetVariables.widgetIndex.name(), widgetIndexVe);
194            variables.put(RenderVariables.widgetVariables.widgetIndex.name() + "_" + level, widgetIndexVe);
195
196            // XXX: expose widget controls too, need to figure out
197            // why controls cannot be references to widget.controls like
198            // properties are in TemplateWidgetTypeHandler
199            if (widget != null) {
200                for (Map.Entry<String, Serializable> ctrl : widget.getControls().entrySet()) {
201                    String key = ctrl.getKey();
202                    String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key;
203                    Serializable value = ctrl.getValue();
204                    variables.put(name, eFactory.createValueExpression(value, Object.class));
205                }
206            }
207
208            List<String> blockedPatterns = new ArrayList<String>();
209            blockedPatterns.add(RenderVariables.widgetVariables.widget.name() + "*");
210            blockedPatterns.add(RenderVariables.widgetVariables.widgetIndex.name() + "*");
211            blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name() + "_*");
212
213            FaceletHandler handler = helper.getAliasFaceletHandler(tagConfigId, variables, blockedPatterns,
214                    nextHandler);
215
216            // apply
217            handler.apply(ctx, parent);
218            widgetCounter++;
219        }
220    }
221
222}