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