001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (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 *     Nuxeo - initial API and implementation
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.platform.ui.web.directory;
021
022import java.io.IOException;
023import java.util.List;
024import java.util.Locale;
025import java.util.Map;
026
027import javax.faces.component.UICommand;
028import javax.faces.component.UIComponent;
029import javax.faces.component.UIOutput;
030import javax.faces.component.UIParameter;
031import javax.faces.context.FacesContext;
032import javax.faces.context.ResponseWriter;
033import javax.faces.el.ValueBinding;
034
035import org.apache.commons.lang.StringUtils;
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.nuxeo.common.utils.i18n.I18NUtils;
039
040/**
041 * @author <a href="mailto:glefter@nuxeo.com">George Lefter</a>
042 */
043public class ChainSelectStatus extends UIOutput {
044
045    public static final String COMPONENT_TYPE = "nxdirectory.chainSelectStatus";
046
047    public static final String COMPONENT_FAMILY = "nxdirectory.chainSelectStatus";
048
049    public static final String REMOVE_ID = "chainSelect_removeId";
050
051    @SuppressWarnings("unused")
052    private static final Log log = LogFactory.getLog(ChainSelectStatus.class);
053
054    private String name;
055
056    private boolean displayIncremental = false;
057
058    private boolean displayRoot = false;
059
060    private String cssStyle;
061
062    private String cssStyleClass;
063
064    private String entryCssStyle;
065
066    private String entryCssStyleClass;
067
068    private String entrySeparator;
069
070    private String image;
071
072    // what to display - the value or the selection of the component
073    private String display;
074
075    // what will be display as a label in front of the select result
076    private String label;
077
078    public String getEntrySeparator() {
079        return entrySeparator;
080    }
081
082    public void setEntrySeparator(String entrySeparator) {
083        this.entrySeparator = entrySeparator;
084    }
085
086    @Override
087    public String getFamily() {
088        return COMPONENT_FAMILY;
089    }
090
091    @Override
092    public String getRendererType() {
093        return null;
094    }
095
096    @Override
097    public void restoreState(FacesContext context, Object state) {
098        Object[] values = (Object[]) state;
099        super.restoreState(context, values[0]);
100        name = (String) values[1];
101        displayIncremental = (Boolean) values[2];
102        displayRoot = (Boolean) values[3];
103        cssStyle = (String) values[4];
104        cssStyleClass = (String) values[5];
105        entryCssStyle = (String) values[6];
106        entryCssStyleClass = (String) values[7];
107        entrySeparator = (String) values[8];
108        display = (String) values[9];
109        image = (String) values[10];
110        label = (String) values[11];
111    }
112
113    @Override
114    public Object saveState(FacesContext arg0) {
115        Object[] values = new Object[12];
116        values[0] = super.saveState(arg0);
117        values[1] = name;
118        values[2] = displayIncremental;
119        values[3] = displayRoot;
120        values[4] = cssStyle;
121        values[5] = cssStyleClass;
122        values[6] = entryCssStyle;
123        values[7] = entryCssStyleClass;
124        values[8] = entrySeparator;
125        values[9] = display;
126        values[10] = image;
127        values[11] = label;
128        return values;
129    }
130
131    @Override
132    public void encodeBegin(FacesContext context) throws IOException {
133        ResponseWriter writer = context.getResponseWriter();
134        String id = getClientId(context);
135
136        String cssStyle = getStringProperty("cssStyle", null);
137        String cssStyleClass = getStringProperty("cssStyleClass", null);
138        String separator = getStringProperty("separator", "/");
139        String entrySeparator = getStringProperty("entrySeparator", null);
140        String label = getStringProperty("label", null);
141
142        ChainSelect chain = getChain();
143        Boolean displayValueOnly = chain.getBooleanProperty("displayValueOnly", false);
144        String display = getStringProperty("display", "selection");
145
146        Selection[] selections;
147        if (display.equals("selection")) {
148            selections = chain.getSelections();
149        } else {
150            selections = chain.getComponentValue();
151        }
152
153        boolean multiParentSelect = chain.getBooleanProperty("multiParentSelect", false);
154
155        if (displayValueOnly) {
156            cssStyle = chain.getStringProperty("displayValueOnlyStyle", null);
157            cssStyleClass = chain.getStringProperty("displayValueOnlyStyleClass", null);
158        }
159
160        writer.startElement("div", this);
161        writer.writeAttribute("id", id, "id");
162        if (cssStyle != null) {
163            writer.writeAttribute("style", cssStyle, "style");
164        }
165        if (cssStyleClass != null) {
166            writer.writeAttribute("class", cssStyleClass, "class");
167        }
168
169        if (selections.length > 0 && label != null) {
170            writer.write(label);
171        }
172
173        for (int i = 0; i < selections.length; i++) {
174            writer.startElement("div", this);
175            if (entryCssStyle != null) {
176                writer.writeAttribute("style", entryCssStyle, "style");
177            }
178            if (entryCssStyleClass != null) {
179                // FIXME: is this a typo? Should it be entryCssStyleClass down there instead of entryCssStyle?
180                writer.writeAttribute("class", entryCssStyle, "class");
181            }
182            if (!displayValueOnly && display.equals("value") && multiParentSelect) {
183                UICommand button = (UICommand) getFacet("removeButton");
184                if (button == null) {
185                    throw new RuntimeException("f:facet with name='removeButton' not found for component " + getId());
186                }
187                String selectionId = selections[i].getValue(chain.getKeySeparator());
188                getUIParameter(button).setValue(selectionId);
189
190                button.encodeBegin(context);
191                button.encodeChildren(context);
192                button.encodeEnd(context);
193            }
194
195            String[] labels = selections[i].getLabels();
196            String[] values = selections[i].getValues();
197            String[] displayedLabels = null;
198            if (labels != null) {
199                displayedLabels = new String[labels.length];
200                for (int j = 0; j < labels.length; j++) {
201                    // boolean localize = chain.getComponent(j)
202                    // .getBooleanProperty("localize", false);
203                    boolean localize;
204                    String compDisplay;
205                    if (chain.compInfos.get(i) != null) {
206                        localize = chain.compInfos.get(i).localize;
207                        compDisplay = chain.compInfos.get(i).display;
208                    } else {
209                        // fallback on the old solution
210                        localize = chain.getComponent(j).getBooleanProperty("localize", false);
211                        compDisplay = chain.getComponent(j).getDisplay();
212                    }
213
214                    String compLabel = labels[j];
215                    if (localize) {
216                        compLabel = translate(context, compLabel);
217                    }
218
219                    if ("id".equals(compDisplay)) {
220                        displayedLabels[j] = values[j];
221                    } else if ("idAndLabel".equals(compDisplay)) {
222                        displayedLabels[j] = values[j] + " " + compLabel;
223                    } else {
224                        // default to "label"
225                        displayedLabels[j] = compLabel;
226                    }
227                }
228            }
229
230            String concatenatedLabel = StringUtils.join(displayedLabels, separator);
231            if (concatenatedLabel.compareTo("") == 0 && displayedLabels.length != 0) {
232                concatenatedLabel = translate(context, "label.directories.error");
233            }
234            writer.write(concatenatedLabel);
235            writer.endElement("div");
236            if (i != selections.length - 1) {
237                if (entrySeparator != null) {
238                    writer.write(entrySeparator);
239                }
240            }
241        }
242    }
243
244    private UIParameter getUIParameter(UIComponent component) {
245        UIParameter param = null;
246        for (UIComponent child : component.getChildren()) {
247            if (child instanceof UIParameter) {
248                UIParameter paramChild = (UIParameter) child;
249                if (REMOVE_ID.equals(paramChild.getName())) {
250                    param = (UIParameter) child;
251                    break;
252                }
253            }
254        }
255        if (param == null) {
256            param = new UIParameter();
257            param.setName(REMOVE_ID);
258            component.getChildren().add(param);
259        }
260
261        return param;
262    }
263
264    @Override
265    public void encodeChildren(FacesContext context) throws IOException {
266    }
267
268    @Override
269    public void encodeEnd(FacesContext context) throws IOException {
270        ResponseWriter writer = context.getResponseWriter();
271        writer.endElement("div");
272
273        List<UIComponent> children = getChildren();
274        for (UIComponent component : children) {
275            component.setRendered(true);
276        }
277    }
278
279    public Object getProperty(String name) {
280        ValueBinding vb = getValueBinding(name);
281        if (vb != null) {
282            return vb.getValue(FacesContext.getCurrentInstance());
283        } else {
284            Map<String, Object> attrMap = getAttributes();
285            return attrMap.get(name);
286        }
287    }
288
289    public String getStringProperty(String name, String defaultValue) {
290        String value = (String) getProperty(name);
291        return value != null ? value : defaultValue;
292    }
293
294    public Boolean getBooleanProperty(String name, Boolean defaultValue) {
295        Boolean value = (Boolean) getProperty(name);
296        return value != null ? value : defaultValue;
297    }
298
299    public String getName() {
300        return name;
301    }
302
303    public void setName(String name) {
304        this.name = name;
305    }
306
307    public ChainSelect getChain() {
308        UIComponent component = getParent();
309        while (component != null && !(component instanceof ChainSelect)) {
310            component = component.getParent();
311        }
312        return (ChainSelect) component;
313    }
314
315    public boolean isDisplayIncremental() {
316        return displayIncremental;
317    }
318
319    public void setDisplayIncremental(boolean displayIncremental) {
320        this.displayIncremental = displayIncremental;
321    }
322
323    public boolean isDisplayRoot() {
324        return displayRoot;
325    }
326
327    public void setDisplayRoot(boolean displayRoot) {
328        this.displayRoot = displayRoot;
329    }
330
331    public String getCssStyle() {
332        return cssStyle;
333    }
334
335    public void setCssStyle(String cssStyle) {
336        this.cssStyle = cssStyle;
337    }
338
339    public String getCssStyleClass() {
340        return cssStyleClass;
341    }
342
343    public void setCssStyleClass(String cssStyleClass) {
344        this.cssStyleClass = cssStyleClass;
345    }
346
347    public String getEntryCssStyle() {
348        return entryCssStyle;
349    }
350
351    public void setEntryCssStyle(String entryCssStyle) {
352        this.entryCssStyle = entryCssStyle;
353    }
354
355    public String getEntryCssStyleClass() {
356        return entryCssStyleClass;
357    }
358
359    public void setEntryCssStyleClass(String entryCssStyleClass) {
360        this.entryCssStyleClass = entryCssStyleClass;
361    }
362
363    public String getImage() {
364        return image;
365    }
366
367    public void setImage(String image) {
368        this.image = image;
369    }
370
371    public String getDisplay() {
372        return display;
373    }
374
375    public void setDisplay(String display) {
376        this.display = display;
377    }
378
379    public String getLabel() {
380        return label;
381    }
382
383    public void setLabel(String label) {
384        this.label = label;
385    }
386
387    protected static String translate(FacesContext context, String label) {
388        String bundleName = context.getApplication().getMessageBundle();
389        Locale locale = context.getViewRoot().getLocale();
390        label = I18NUtils.getMessageString(bundleName, label, null, locale);
391        return label;
392    }
393
394}