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