001/*
002 * (C) Copyright 2006-2014 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: AbstractWidgetTypeHandler.java 28491 2008-01-04 19:04:30Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.forms.layout.facelets.plugins;
023
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Map;
028
029import javax.faces.component.UIComponent;
030import javax.faces.view.facelets.CompositeFaceletHandler;
031import javax.faces.view.facelets.FaceletContext;
032import javax.faces.view.facelets.FaceletHandler;
033import javax.faces.view.facelets.TagAttribute;
034import javax.faces.view.facelets.TagAttributes;
035import javax.faces.view.facelets.TagConfig;
036import javax.faces.view.facelets.ValidatorHandler;
037
038import org.apache.commons.lang.StringUtils;
039import org.nuxeo.ecm.core.api.validation.DocumentValidationService;
040import org.nuxeo.ecm.platform.forms.layout.api.BuiltinWidgetModes;
041import org.nuxeo.ecm.platform.forms.layout.api.Widget;
042import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException;
043import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper;
044import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables;
045import org.nuxeo.ecm.platform.forms.layout.facelets.WidgetTypeHandler;
046import org.nuxeo.ecm.platform.forms.layout.facelets.dev.WidgetTypeDevTagHandler;
047import org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler;
048import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory;
049import org.nuxeo.ecm.platform.ui.web.validator.DocumentConstraintValidator;
050import org.nuxeo.runtime.api.Framework;
051
052import com.sun.faces.facelets.tag.ui.InsertHandler;
053
054/**
055 * Abstract widget type handler.
056 *
057 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
058 */
059public abstract class AbstractWidgetTypeHandler extends WidgetTypeHandler {
060
061    /**
062     * @since 6.0
063     */
064    public static final String DEV_TEMPLATE_PROPERTY_NAME = "dev_template";
065
066    /**
067     * @since 6.0
068     */
069    public static final String DISABLE_DEV_PROPERTY_NAME = "disable_dev";
070
071    protected final TagConfig tagConfig;
072
073    protected Map<String, String> properties;
074
075    protected Widget widget;
076
077    protected final TagAttribute widgetAttr;
078
079    public AbstractWidgetTypeHandler(TagConfig config) {
080        super(config);
081        tagConfig = config;
082        widgetAttr = getAttribute("widget");
083    }
084
085    @Override
086    public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
087        if (widget == null) {
088            // resolve widget attribute instead, useful for the runtime tag library
089            Widget wi = (Widget) widgetAttr.getObject(ctx, Widget.class);
090            if (wi != null) {
091                apply(ctx, parent, wi);
092            }
093        } else {
094            apply(ctx, parent, widget);
095        }
096    }
097
098    public abstract void apply(FaceletContext ctx, UIComponent parent, Widget widget) throws WidgetException,
099            IOException;
100
101    public FaceletHandler getDevFaceletHandler(TagConfig tagConfig, Widget widget) throws WidgetException {
102        if (Boolean.parseBoolean(getProperty(DISABLE_DEV_PROPERTY_NAME))
103                || Boolean.parseBoolean((String) widget.getProperty(DISABLE_DEV_PROPERTY_NAME))) {
104            return null;
105        }
106        // lookup in the widget type configuration
107        String template = (String) widget.getProperty(DEV_TEMPLATE_PROPERTY_NAME);
108        if (StringUtils.isBlank(template)) {
109            template = getProperty(DEV_TEMPLATE_PROPERTY_NAME);
110        }
111        FaceletHandlerHelper helper = new FaceletHandlerHelper(tagConfig);
112        TagAttribute widgetAttr = helper.createAttribute("widget",
113                "#{" + RenderVariables.widgetVariables.widget.name() + "}");
114        TagAttributes devWidgetAttributes;
115        if (StringUtils.isBlank(template)) {
116            devWidgetAttributes = FaceletHandlerHelper.getTagAttributes(widgetAttr);
117        } else {
118            devWidgetAttributes = FaceletHandlerHelper.getTagAttributes(widgetAttr,
119                    helper.createAttribute("template", template));
120        }
121        TagConfig devWidgetConfig = TagConfigFactory.createTagConfig(tagConfig, widget.getTagConfigId(),
122                devWidgetAttributes, new LeafFaceletHandler());
123        return new WidgetTypeDevTagHandler(devWidgetConfig);
124    }
125
126    public String getProperty(String name) {
127        if (properties != null) {
128            return properties.get(name);
129        }
130        return null;
131    }
132
133    /**
134     * Helper method, throws an exception if property value is null.
135     */
136    public String getRequiredProperty(String name) throws WidgetException {
137        String value = getProperty(name);
138        if (value == null) {
139            throw new WidgetException("Required property '" + name + "' is missing on widget type configuration");
140        }
141        return value;
142    }
143
144    public void setProperties(Map<String, String> properties) {
145        this.properties = properties;
146    }
147
148    public void setWidget(Widget widget) {
149        this.widget = widget;
150    }
151
152    /**
153     * Returns sub handlers as computed from tag information.
154     * <p>
155     * Adds an sub insert handler slot named {@link RenderVariables.widgetTemplatingZones#inside_input_widget} when
156     * widget is in edit mode.
157     * <p>
158     * Adds an sub document constraint validator handler named {@link DocumentConstraintValidator#VALIDATOR_ID} when
159     * widget is in edit mode.
160     * <p>
161     *
162     * @since 6.0
163     */
164    protected FaceletHandler getNextHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget,
165            FaceletHandler[] subHandlers, FaceletHandlerHelper helper) {
166        boolean isEdit = BuiltinWidgetModes.EDIT.equals(widget.getMode());
167        return getNextHandler(ctx, tagConfig, widget, subHandlers, helper, isEdit, isEdit);
168    }
169
170    /**
171     * Returns sub handlers as computed from tag information.
172     * <p>
173     * Adds an input slot if corresponding boolean parameter is true.
174     * <p>
175     * Adds an document constraint validator if corresponding boolean parameter is true.
176     *
177     * @since 7.2
178     */
179    protected FaceletHandler getNextHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget,
180            FaceletHandler[] subHandlers, FaceletHandlerHelper helper, boolean addInputSlot,
181            boolean addDocumentConstraintValidator) {
182        FaceletHandler leaf;
183        List<FaceletHandler> handlers = new ArrayList<>();
184        if (nextHandler != null && !(nextHandler instanceof LeafFaceletHandler)) {
185            handlers.add(nextHandler);
186        }
187        if (subHandlers != null && subHandlers.length > 0) {
188            for (FaceletHandler fh : subHandlers) {
189                if (fh != null && !(fh instanceof LeafFaceletHandler)) {
190                    handlers.add(fh);
191                }
192            }
193        }
194        if (addInputSlot) {
195            FaceletHandler slot = getInputSlotHandler(ctx, tagConfig, widget, subHandlers, helper);
196            if (slot != null) {
197                handlers.add(slot);
198            }
199        }
200        DocumentValidationService validationService = Framework.getService(DocumentValidationService.class);
201        if (addDocumentConstraintValidator
202                && validationService.isActivated(DocumentConstraintValidator.CTX_JSFVALIDATOR, null)) {
203            FaceletHandler v = getDocumentConstraintValidatorHandler(ctx, tagConfig, widget, subHandlers, helper);
204            if (v != null) {
205                handlers.add(v);
206            }
207        }
208        if (handlers.size() == 0) {
209            leaf = new LeafFaceletHandler();
210        } else {
211            leaf = new CompositeFaceletHandler(handlers.toArray(new FaceletHandler[] {}));
212        }
213        return leaf;
214    }
215
216    protected FaceletHandler getInputSlotHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget,
217            FaceletHandler[] subHandlers, FaceletHandlerHelper helper) {
218        TagConfig config = TagConfigFactory.createTagConfig(tagConfig, tagConfig.getTagId(),
219                FaceletHandlerHelper.getTagAttributes(helper.createAttribute("name",
220                        RenderVariables.widgetTemplatingZones.inside_input_widget.name())), new LeafFaceletHandler());
221        return new InsertHandler(config);
222    }
223
224    protected FaceletHandler getDocumentConstraintValidatorHandler(FaceletContext ctx, TagConfig tagConfig,
225            Widget widget, FaceletHandler[] subHandlers, FaceletHandlerHelper helper) {
226        // XXX maybe take into account control on widget to handle sub properties validation (or not)
227        ValidatorHandler validator = helper.getValidateHandler(tagConfig.getTagId(),
228                FaceletHandlerHelper.getTagAttributes(), new LeafFaceletHandler(),
229                DocumentConstraintValidator.VALIDATOR_ID);
230        return validator;
231    }
232
233}