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