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}