001/*
002 * Copyright 2004 The Apache Software Foundation.
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 */
016package org.nuxeo.ecm.platform.ui.web.component;
017
018import java.util.Collections;
019import java.util.List;
020import java.util.Locale;
021import java.util.Map;
022
023import javax.el.ValueExpression;
024import javax.faces.context.FacesContext;
025import javax.faces.model.SelectItem;
026
027import org.apache.commons.lang.StringUtils;
028import org.nuxeo.common.utils.i18n.I18NUtils;
029import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils;
030
031/**
032 * EasySelectItems from http://jsf-comp.sourceforge.net/components/easysi/index.html, adapted to work with jboss seam
033 * ListDataModel instances.
034 * <p>
035 * Adapted to handle ordering and disabling of select items.
036 *
037 * @author Cagatay-Mert
038 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
039 */
040public class UISelectItems extends javax.faces.component.UISelectItems implements ResettableComponent {
041
042    public static final String COMPONENT_TYPE = UISelectItems.class.getName();
043
044    protected enum PropertyKeys {
045        value, var, itemLabel, itemLabels, resolveItemLabelTwice,
046        //
047        itemLabelPrefix, itemLabelPrefixSeparator,
048        //
049        itemLabelSuffix, itemLabelSuffixSeparator,
050        //
051        itemValue, itemRendered, itemDisabled, itemEscaped,
052        //
053        ordering, caseSensitive,
054        //
055        displayIdAndLabel, displayIdAndLabelSeparator,
056        //
057        localize, dbl10n;
058    }
059
060    public String getVar() {
061        return (String) getStateHelper().eval(PropertyKeys.var);
062    }
063
064    public void setVar(String var) {
065        getStateHelper().put(PropertyKeys.var, var);
066    }
067
068    public Object getItemLabel() {
069        return getStateHelper().eval(PropertyKeys.itemLabel);
070    }
071
072    public void setItemLabel(Object itemLabel) {
073        getStateHelper().put(PropertyKeys.itemLabel, itemLabel);
074    }
075
076    @SuppressWarnings("unchecked")
077    public Map<String, String> getItemLabels() {
078        return (Map<String, String>) getStateHelper().eval(PropertyKeys.itemLabels);
079    }
080
081    public void setItemLabels(Map<String, String> itemLabels) {
082        getStateHelper().put(PropertyKeys.itemLabels, itemLabels);
083    }
084
085    public String getItemLabelPrefix() {
086        return (String) getStateHelper().eval(PropertyKeys.itemLabelPrefix);
087    }
088
089    public void setItemLabelPrefix(String itemLabelPrefix) {
090        getStateHelper().put(PropertyKeys.itemLabelPrefix, itemLabelPrefix);
091    }
092
093    public String getItemLabelPrefixSeparator() {
094        return (String) getStateHelper().eval(PropertyKeys.itemLabelPrefixSeparator, " ");
095    }
096
097    public void setItemLabelPrefixSeparator(String itemLabelPrefix) {
098        getStateHelper().put(PropertyKeys.itemLabelPrefixSeparator, itemLabelPrefix);
099    }
100
101    public String getItemLabelSuffix() {
102        return (String) getStateHelper().eval(PropertyKeys.itemLabelSuffix);
103    }
104
105    public void setItemLabelSuffix(String itemLabelSuffix) {
106        getStateHelper().put(PropertyKeys.itemLabelSuffix, itemLabelSuffix);
107    }
108
109    public String getItemLabelSuffixSeparator() {
110        return (String) getStateHelper().eval(PropertyKeys.itemLabelSuffixSeparator, " ");
111    }
112
113    public void setItemLabelSuffixSeparator(String itemLabelSuffix) {
114        getStateHelper().put(PropertyKeys.itemLabelSuffixSeparator, itemLabelSuffix);
115    }
116
117    public Object getItemValue() {
118        return getStateHelper().eval(PropertyKeys.itemValue);
119    }
120
121    public void setItemValue(Object itemValue) {
122        getStateHelper().put(PropertyKeys.itemValue, itemValue);
123    }
124
125    public boolean isItemDisabled() {
126        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.itemDisabled, Boolean.FALSE));
127    }
128
129    @SuppressWarnings("boxing")
130    public void setItemDisabled(boolean itemDisabled) {
131        getStateHelper().put(PropertyKeys.itemDisabled, itemDisabled);
132    }
133
134    public boolean isResolveItemLabelTwice() {
135        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.resolveItemLabelTwice, Boolean.FALSE));
136    }
137
138    @SuppressWarnings("boxing")
139    public void setResolveItemLabelTwice(boolean resolveItemLabelTwice) {
140        getStateHelper().put(PropertyKeys.resolveItemLabelTwice, resolveItemLabelTwice);
141    }
142
143    public boolean isItemRendered() {
144        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.itemRendered, Boolean.TRUE));
145    }
146
147    @SuppressWarnings("boxing")
148    public void setItemRendered(boolean itemRendered) {
149        getStateHelper().put(PropertyKeys.itemRendered, itemRendered);
150    }
151
152    public boolean isItemEscaped() {
153        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.itemEscaped, Boolean.FALSE));
154    }
155
156    @SuppressWarnings("boxing")
157    public void setItemEscaped(boolean itemEscaped) {
158        getStateHelper().put(PropertyKeys.itemEscaped, itemEscaped);
159    }
160
161    public String getOrdering() {
162        return (String) getStateHelper().eval(PropertyKeys.ordering);
163    }
164
165    public void setOrdering(String ordering) {
166        getStateHelper().put(PropertyKeys.ordering, ordering);
167    }
168
169    public boolean isCaseSensitive() {
170        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.caseSensitive));
171    }
172
173    @SuppressWarnings("boxing")
174    public void setCaseSensitive(boolean caseSensitive) {
175        getStateHelper().put(PropertyKeys.caseSensitive, caseSensitive);
176    }
177
178    public boolean isDisplayIdAndLabel() {
179        return Boolean.TRUE.equals(getStateHelper().eval(PropertyKeys.displayIdAndLabel));
180    }
181
182    @SuppressWarnings("boxing")
183    public void setDisplayIdAndLabel(boolean displayIdAndLabel) {
184        getStateHelper().put(PropertyKeys.displayIdAndLabel, displayIdAndLabel);
185    }
186
187    public String getDisplayIdAndLabelSeparator() {
188        return (String) getStateHelper().eval(PropertyKeys.displayIdAndLabelSeparator, " ");
189    }
190
191    public void setDisplayIdAndLabelSeparator(String idAndLabelSeparator) {
192        getStateHelper().put(PropertyKeys.displayIdAndLabelSeparator, idAndLabelSeparator);
193    }
194
195    @SuppressWarnings("boxing")
196    public boolean isLocalize() {
197        return (Boolean) getStateHelper().eval(PropertyKeys.localize, Boolean.FALSE);
198    }
199
200    @SuppressWarnings("boxing")
201    public void setLocalize(boolean localize) {
202        getStateHelper().put(PropertyKeys.localize, localize);
203    }
204
205    @SuppressWarnings("boxing")
206    public boolean isdbl10n() {
207        return (Boolean) getStateHelper().eval(PropertyKeys.dbl10n, Boolean.FALSE);
208    }
209
210    @SuppressWarnings("boxing")
211    public void setdbl10n(boolean dbl10n) {
212        getStateHelper().put(PropertyKeys.dbl10n, dbl10n);
213    }
214
215    @Override
216    public Object getValue() {
217        Object value = super.getValue();
218        List<SelectItem> items = new SelectItemsFactory() {
219            @Override
220            protected String getVar() {
221                return UISelectItems.this.getVar();
222            }
223
224            @Override
225            protected SelectItem createSelectItem() {
226                return UISelectItems.this.createSelectItem();
227            }
228
229        }.createSelectItems(value);
230
231        String ordering = getOrdering();
232        boolean caseSensitive = isCaseSensitive();
233        if (!StringUtils.isBlank(ordering)) {
234            Collections.sort(items, new SelectItemComparator(ordering, Boolean.valueOf(caseSensitive)));
235        }
236        return items.toArray(new SelectItem[0]);
237    }
238
239    protected String translate(FacesContext context, Locale locale, String label) {
240        if (StringUtils.isBlank(label)) {
241            return label;
242        }
243        String bundleName = context.getApplication().getMessageBundle();
244        label = I18NUtils.getMessageString(bundleName, label, null, locale);
245        return label;
246    }
247
248    protected String retrieveItemLabel() {
249        FacesContext ctx = FacesContext.getCurrentInstance();
250        Locale locale = ctx.getViewRoot().getLocale();
251        String label = null;
252        if (isdbl10n()) {
253            Map<String, String> labels = getItemLabels();
254            if (labels != null) {
255                if (labels.containsKey(locale.getLanguage())) {
256                    label = labels.get(locale.getLanguage());
257                } else {
258                    // fallback on en
259                    label = labels.get("en");
260                }
261            }
262        }
263        if (StringUtils.isBlank(label)) {
264            Object labelObject = getItemLabel();
265            label = labelObject != null ? labelObject.toString() : null;
266        }
267        if (isResolveItemLabelTwice() && ComponentTagUtils.isValueReference(label)) {
268            ValueExpression ve = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(),
269                    label, Object.class);
270            if (ve != null) {
271                Object newLabel = ve.getValue(ctx.getELContext());
272                if (newLabel instanceof String) {
273                    label = (String) newLabel;
274                }
275            }
276        }
277        if (isLocalize()) {
278            label = translate(ctx, locale, label);
279        }
280        return label;
281    }
282
283    protected SelectItem createSelectItem() {
284        if (!isItemRendered()) {
285            return null;
286        }
287        Object value = getItemValue();
288        String label = retrieveItemLabel();
289        if (isDisplayIdAndLabel() && label != null) {
290            label = value + getDisplayIdAndLabelSeparator() + label;
291        }
292        // make sure label is never blank
293        if (StringUtils.isBlank(label)) {
294            label = String.valueOf(value);
295        }
296        String labelPrefix = getItemLabelPrefix();
297        if (!StringUtils.isBlank(labelPrefix)) {
298            label = labelPrefix + getItemLabelPrefixSeparator() + label;
299        }
300        String labelSuffix = getItemLabelSuffix();
301        if (!StringUtils.isBlank(labelSuffix)) {
302            label = label + getItemLabelSuffixSeparator() + labelSuffix;
303        }
304        return new SelectItem(value, label, null, isItemDisabled(), isItemEscaped());
305    }
306
307    /**
308     * Reset the local value set, useful to reset cache on ajax action when using a shuttle widget for instance.
309     *
310     * @since 5.7
311     */
312    @Override
313    public void resetCachedModel() {
314        setValue(null);
315    }
316
317}