001/*
002 * (C) Copyright 2010 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Anahide Tchertchian
016 */
017package org.nuxeo.ecm.platform.forms.layout.demo.jsf;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import javax.faces.application.FacesMessage;
026import javax.faces.component.UIComponent;
027import javax.faces.context.FacesContext;
028import javax.faces.validator.ValidatorException;
029
030import org.apache.commons.lang.StringUtils;
031import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition;
032import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
033import org.nuxeo.ecm.platform.forms.layout.api.impl.FieldDefinitionImpl;
034
035/**
036 * Collects information to generate a layout definition from user information.
037 *
038 * @author Anahide Tchertchian
039 * @since 5.4
040 */
041public class PreviewLayoutDefinition implements Serializable {
042
043    private static final long serialVersionUID = 1L;
044
045    protected final String widgetType;
046
047    protected final List<String> fields;
048
049    protected String label;
050
051    protected String helpLabel;
052
053    protected Boolean translated;
054
055    protected Boolean handlingLabels;
056
057    protected Map<String, Serializable> defaultProperties;
058
059    protected Map<String, Serializable> properties;
060
061    protected List<Map<String, Serializable>> customProperties;
062
063    protected List<WidgetDefinition> subWidgets;
064
065    public PreviewLayoutDefinition(String widgetType, List<String> fields, Map<String, Serializable> defaultProperties) {
066        super();
067        this.widgetType = widgetType;
068        this.fields = fields;
069        this.defaultProperties = defaultProperties;
070    }
071
072    public String getWidgetType() {
073        return widgetType;
074    }
075
076    public List<String> getFields() {
077        return fields;
078    }
079
080    public List<FieldDefinition> getFieldDefinitions() {
081        if (fields != null) {
082            List<FieldDefinition> res = new ArrayList<FieldDefinition>();
083            for (String field : fields) {
084                res.add(new FieldDefinitionImpl(null, field));
085            }
086            return res;
087        }
088        return null;
089    }
090
091    public String getLabel() {
092        return label;
093    }
094
095    public void setLabel(String label) {
096        this.label = label;
097    }
098
099    public String getHelpLabel() {
100        return helpLabel;
101    }
102
103    public void setHelpLabel(String helpLabel) {
104        this.helpLabel = helpLabel;
105    }
106
107    public Boolean getTranslated() {
108        return translated;
109    }
110
111    public void setTranslated(Boolean translated) {
112        this.translated = translated;
113    }
114
115    public Boolean getHandlingLabels() {
116        return handlingLabels;
117    }
118
119    public void setHandlingLabels(Boolean handlingLabels) {
120        this.handlingLabels = handlingLabels;
121    }
122
123    public List<WidgetDefinition> getSubWidgets() {
124        return subWidgets;
125    }
126
127    public void setSubWidgets(List<WidgetDefinition> subWidgets) {
128        this.subWidgets = subWidgets;
129    }
130
131    public Map<String, Serializable> getProperties() {
132        if (properties == null) {
133            properties = new HashMap<String, Serializable>();
134            // fill with default properties
135            if (defaultProperties != null) {
136                properties.putAll(defaultProperties);
137            }
138        }
139        return properties;
140    }
141
142    public void setProperties(Map<String, Serializable> properties) {
143        this.properties = properties;
144    }
145
146    public List<Map<String, Serializable>> getCustomProperties() {
147        if (customProperties == null) {
148            customProperties = new ArrayList<Map<String, Serializable>>();
149        }
150        return customProperties;
151    }
152
153    public void setCustomProperties(List<Map<String, Serializable>> customProperties) {
154        this.customProperties = customProperties;
155    }
156
157    public Map<String, Serializable> getWidgetProperties() {
158        Map<String, Serializable> widgetProps = new HashMap<String, Serializable>();
159        Map<String, Serializable> props = getProperties();
160        if (props != null) {
161            widgetProps.putAll(props);
162        }
163        List<Map<String, Serializable>> customProps = getCustomProperties();
164        if (customProps != null) {
165            widgetProps.putAll(convertCustomProperties(customProps, true));
166        }
167        return cleanUpProperties(widgetProps);
168    }
169
170    /**
171     * Removes empty properties as the JSF component may not accept empty values for some properties like "converter" or
172     * "validator".
173     */
174    protected Map<String, Serializable> cleanUpProperties(Map<String, Serializable> props) {
175        Map<String, Serializable> res = new HashMap<String, Serializable>();
176        if (props != null) {
177            for (Map.Entry<String, Serializable> prop : props.entrySet()) {
178                Serializable value = prop.getValue();
179                if (value == null || (value instanceof String && StringUtils.isEmpty((String) value))) {
180                    continue;
181                }
182                res.put(prop.getKey(), value);
183            }
184        }
185        return res;
186    }
187
188    public Map<String, Serializable> getNewCustomProperty() {
189        Map<String, Serializable> prop = new HashMap<String, Serializable>();
190        prop.put("key", null);
191        prop.put("value", null);
192        return prop;
193    }
194
195    protected Map<String, Serializable> convertCustomProperties(List<Map<String, Serializable>> listProps,
196            boolean ignoreErrors) throws ValidatorException {
197        Map<String, Serializable> values = new HashMap<String, Serializable>();
198        if (listProps != null) {
199            for (Map<String, Serializable> entry : listProps) {
200                String key = (String) entry.get("key");
201                Serializable value = entry.get("value");
202                if (key == null || key.trim().length() == 0) {
203                    if (ignoreErrors) {
204                        continue;
205                    }
206                    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid empty key", null);
207                    throw new ValidatorException(message);
208                }
209                if (values.containsKey(key)) {
210                    if (ignoreErrors) {
211                        continue;
212                    }
213                    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, String.format(
214                            "Duplicate key '%s'", key), null);
215                    throw new ValidatorException(message);
216                }
217                values.put(key, value);
218            }
219        }
220        return values;
221    }
222
223    @SuppressWarnings({ "unchecked", "rawtypes" })
224    public void validateCustomProperties(FacesContext context, UIComponent component, Object value) {
225        if (value != null && !(value instanceof List)) {
226            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid value: " + value, null);
227            // also add global message
228            context.addMessage(null, message);
229            throw new ValidatorException(message);
230        }
231        List<Map<String, Serializable>> listValue = (List) value;
232        // will throw an error
233        convertCustomProperties(listValue, false);
234    }
235
236}