001/*
002 * (C) Copyright 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: EditableListBean.java 25566 2007-10-01 14:01:21Z atchertchian $
020 */
021
022package org.nuxeo.ecm.platform.ui.web.component.list;
023
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import javax.faces.component.UIComponent;
030import javax.faces.context.ExternalContext;
031import javax.faces.context.FacesContext;
032import javax.faces.event.ActionEvent;
033import javax.faces.event.AjaxBehaviorEvent;
034import javax.faces.event.FacesEvent;
035
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.nuxeo.ecm.platform.ui.web.model.EditableModel;
039
040/**
041 * Bean used to interact with {@link UIEditableList} component.
042 * <p>
043 * Used to add/remove items from a list.
044 * <p>
045 * Optionally used to work around some unwanted behaviour in data tables.
046 *
047 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
048 */
049public class EditableListBean {
050
051    private static final Log log = LogFactory.getLog(EditableListBean.class);
052
053    public static final String FOR_PARAMETER_NAME = "for";
054
055    public static final String INDEX_PARAMETER_NAME = "index";
056
057    public static final String TYPE_PARAMETER_NAME = "type";
058
059    public static final String NUMBER_PARAMETER_NAME = "number";
060
061    protected UIComponent binding;
062
063    // dont make it static so that jsf can call it
064    public UIComponent getBinding() {
065        return binding;
066    }
067
068    // dont make it static so that jsf can call it
069    public void setBinding(UIComponent binding) {
070        this.binding = binding;
071    }
072
073    // don't make it static so that jsf can call it
074    public void performAction(String listComponentId, String index, String type) {
075        if (binding == null) {
076            log.error("Component binding not set, cannot perform action");
077            return;
078        }
079        Map<String, String> requestMap = new HashMap<String, String>();
080        requestMap.put(FOR_PARAMETER_NAME, listComponentId);
081        requestMap.put(INDEX_PARAMETER_NAME, index);
082        requestMap.put(TYPE_PARAMETER_NAME, type);
083        performAction(binding, requestMap);
084    }
085
086    public void performAction(ActionEvent event) {
087        performAction((FacesEvent) event);
088    }
089
090    /**
091     * @since 6.0
092     */
093    public void performAction(AjaxBehaviorEvent event) {
094        performAction((FacesEvent) event);
095    }
096
097    protected void performAction(FacesEvent event) {
098        UIComponent component = event.getComponent();
099        if (component == null) {
100            return;
101        }
102        FacesContext context = FacesContext.getCurrentInstance();
103        ExternalContext eContext = context.getExternalContext();
104        Map<String, String> requestMap = eContext.getRequestParameterMap();
105        performAction(component, requestMap);
106    }
107
108    protected static void performAction(UIComponent binding, Map<String, String> requestMap) {
109        UIEditableList editableComp = getEditableListComponent(binding, requestMap);
110        if (editableComp == null) {
111            return;
112        }
113        EditableListModificationType type = getModificationType(requestMap);
114        if (type == null) {
115            return;
116        }
117        Integer index;
118        Integer number;
119
120        EditableModel model = editableComp.getEditableModel();
121        Object template = editableComp.getTemplate();
122        switch (type) {
123        case ADD:
124            number = getNumber(requestMap);
125            if (number == null) {
126                // perform add only once
127                model.addValue(template);
128            } else {
129                for (int i = 0; i < number; i++) {
130                    // make sure added template is unreferenced
131                    model.addTemplateValue();
132                }
133            }
134            break;
135        case INSERT:
136            index = getIndex(requestMap);
137            if (index == null) {
138                return;
139            }
140            number = getNumber(requestMap);
141            if (number == null) {
142                // perform insert only once
143                model.insertValue(index, template);
144            } else {
145                for (int i = 0; i < number; i++) {
146                    model.insertTemplateValue(index);
147                }
148            }
149            break;
150        case REMOVE:
151            index = getIndex(requestMap);
152            if (index == null) {
153                return;
154            }
155            editableComp.removeValue(index);
156            break;
157        case MOVEUP:
158            index = getIndex(requestMap);
159            if (index == null) {
160                return;
161            }
162            editableComp.moveValue(index, index - 1);
163            break;
164        case MOVEDOWN:
165            index = getIndex(requestMap);
166            if (index == null) {
167                return;
168            }
169            editableComp.moveValue(index, index + 1);
170            break;
171        }
172    }
173
174    protected static String getParameterValue(Map<String, String> requestMap, String parameterName) {
175        String string = requestMap.get(parameterName);
176        if (string == null || string.length() == 0) {
177            return null;
178        } else {
179            return string;
180        }
181    }
182
183    protected static UIEditableList getEditableListComponent(UIComponent component, Map<String, String> requestMap) {
184        UIEditableList listComponent = null;
185        String forString = getParameterValue(requestMap, FOR_PARAMETER_NAME);
186        if (forString == null) {
187            log.error("Could not find '" + FOR_PARAMETER_NAME + "' parameter in the request map");
188        } else {
189            UIComponent forComponent = component.findComponent(forString);
190            if (forComponent == null) {
191                log.error("Could not find component with id: " + forString);
192            } else if (!(forComponent instanceof UIEditableList)) {
193                log.error("Invalid component with id " + forString + ": " + forComponent
194                        + ", expected a component with class " + UIEditableList.class);
195            } else {
196                listComponent = (UIEditableList) forComponent;
197            }
198        }
199        return listComponent;
200    }
201
202    protected static EditableListModificationType getModificationType(Map<String, String> requestMap) {
203        EditableListModificationType type = null;
204        String typeString = getParameterValue(requestMap, TYPE_PARAMETER_NAME);
205        if (typeString == null) {
206            log.error("Could not find '" + TYPE_PARAMETER_NAME + "' parameter in the request map");
207        } else {
208            try {
209                type = EditableListModificationType.valueOfString(typeString);
210            } catch (IllegalArgumentException err) {
211                log.error("Illegal value for '" + TYPE_PARAMETER_NAME + "' attribute: " + typeString
212                        + ", should be one of " + EditableListModificationType.values());
213            }
214        }
215        return type;
216    }
217
218    protected static Integer getIndex(Map<String, String> requestMap) {
219        Integer index = null;
220        String indexString = getParameterValue(requestMap, INDEX_PARAMETER_NAME);
221        if (indexString == null) {
222            log.error("Could not find '" + INDEX_PARAMETER_NAME + "' parameter in the request map");
223        } else {
224            try {
225                index = Integer.valueOf(indexString);
226            } catch (NumberFormatException e) {
227                log.error("Illegal value for '" + INDEX_PARAMETER_NAME + "' attribute: " + indexString
228                        + ", should be integer");
229            }
230        }
231        return index;
232    }
233
234    protected static Integer getNumber(Map<String, String> requestMap) {
235        Integer number = null;
236        String numberString = getParameterValue(requestMap, NUMBER_PARAMETER_NAME);
237        if (numberString != null) {
238            try {
239                number = Integer.valueOf(numberString);
240            } catch (NumberFormatException e) {
241                log.error("Illegal value for '" + NUMBER_PARAMETER_NAME + "' attribute: " + numberString
242                        + ", should be integer");
243            }
244        }
245        return number;
246    }
247
248    /**
249     * Dummy list of one item, used to wrap a table within another table.
250     * <p>
251     * A table resets its saved state when decoding, which is a problem when saving a file temporarily: as it will not
252     * be submitted again in the request, the new value will be lost. The table is not reset when embedded in another
253     * table, so we can use this list as value of the embedding table as a work around.
254     *
255     * @return dummy list of one item
256     */
257    // don't make it static so that jsf can call it
258    public List<Object> getDummyList() {
259        List<Object> dummy = new ArrayList<Object>(1);
260        dummy.add("dummy");
261        return dummy;
262    }
263
264}