001 package net.sf.jolene.dom; 002 003 import net.sf.jolene.constants.Elements; 004 import net.sf.jolene.constants.Tags; 005 import net.sf.jolene.html.Attributes; 006 import org.apache.commons.beanutils.BeanUtils; 007 008 import java.util.*; 009 010 /** 011 * A form in a html document. This acts as a container for other html objects. 012 * 013 * @author Dan Howard 014 * @since Oct 21, 2003 015 */ 016 public final class Form extends HTMLElement { 017 018 List<HTMLElement> elements; 019 020 Form() { 021 tag = Tags.form; 022 elements = new ArrayList<HTMLElement>(16); 023 } 024 025 026 @Override 027 public Form clone() { 028 Form form = (Form) super.clone(); 029 for (int j = 0; j < elements.size(); j++) { 030 HTMLElement element = elements(j).clone(); 031 form.addElement(element); 032 } 033 return form; 034 } 035 036 037 /** 038 * This method returns the widget specified by the numeric index. 039 * The prefered way of retrieving an object from the form is to 040 * use it's name or id instead since the order of the objects can be 041 * changed easily by a page designer. 042 * 043 * @param element - The integer element number of the object desired. 044 * @return HTMLElement 045 */ 046 public HTMLElement elements(int element) { 047 return elements.get(element); 048 } 049 050 /** 051 * This method retrieves an object from the document by name or id. 052 * When the document is opened it creates objects by looking at the ID 053 * attribute first. If that attribute doesn't exist it uses the NAME 054 * attribute. If neither exist then the only way to address these 055 * objects is by number (not recommended).<br> 056 * This method returns the base HTMLElement object so if you want a Select or 057 * Grid object you can either cast it: 058 * <pre> 059 * Grid grid grid = (Grid)document.forms(0).elements("Grid1"); 060 * </pre> 061 * or use the appropriate getter: 062 * <pre> 063 * Grid grid = document.forms(0).getGrid("Grid1"); 064 * </pre> 065 * 066 * @param elementName - The string name or id of the object desired. 067 * @return HTMLElement or null if not found or elementName is null. 068 */ 069 public HTMLElement elements(String elementName) { 070 071 if (elementName == null) { 072 return null; 073 } 074 075 // locate the object whose name == this string. 076 String name = ""; 077 HTMLElement element; 078 int j; 079 080 for (j = 0; j < elements.size(); j++) { 081 082 if (elementName.equalsIgnoreCase(elements.get(j).getName())) { 083 break; 084 } 085 /* 086 if (element.hasAttribute("id")) { 087 name = element.getAttribute("id"); 088 if (name.equalsIgnoreCase(elementName)) { 089 break; // found it. 090 } 091 } else if (element.hasAttribute("name")) { 092 name = (String) element.getAttribute("name"); 093 if (name.equalsIgnoreCase(elementName)) { 094 break; // found it. 095 } 096 } else { 097 // We have to throw an exception here? We have an object with no NAME!!!!! 098 099 } 100 */ 101 102 } 103 104 if (j < elements.size()) { 105 return elements(j); 106 } else { 107 // THROW EXCEPTION 108 // Based on the HashMap, no exception is thrown but a null is returned which 109 // will cause a null pointer exception later. 110 return null; 111 } 112 } 113 114 115 /** 116 * @return Number of elements in the form. 117 */ 118 public int getElementCount() { 119 return elements.size(); 120 } 121 122 /** 123 * Returns the specified element casted to a Grid object. 124 * 125 * @param name name of grid 126 * @return Grid 127 */ 128 public Grid getGrid(String name) { 129 return (Grid) elements(name); 130 } 131 132 /** 133 * Returns a Map of Radio objects based the specified radio group name. 134 * The map's order is insertion-order. 135 * 136 * @param groupName of radios 137 * @return Map<String, HTMLElement> 138 */ 139 public Map<String, HTMLElement> getRadioGroup(String groupName) { 140 Map<String, HTMLElement> map = new LinkedHashMap<String, HTMLElement>(3); 141 // loop through the objects for radios with the specified name 142 // and add each to a hashmap based on their VALUE attribute. 143 // If no value exists, log a warn and add each with an integer as 144 // the key. 145 for (int j = 0; j < elements.size(); j++) { 146 HTMLElement element = elements.get(j); 147 if (element instanceof Radio) { 148 if (groupName.equalsIgnoreCase(element.getName())) { 149 log.debug("Radio " + groupName + " found."); 150 String key; 151 if (element.hasAttribute("value")) { 152 key = element.getAttribute("value"); 153 log.debug("Radio " + groupName + " key " + key); 154 } else { 155 key = String.valueOf(j); 156 log.debug("getRadioGroup: Radio has no value: " + element + " assigning " + key); 157 } 158 map.put(key, element); 159 } 160 } 161 } 162 return map; 163 } 164 165 /** 166 * Returns the specified element casted to a Select object. 167 * 168 * @param name name of select 169 * @return Select 170 */ 171 public Select getSelect(String name) { 172 return (Select) elements(name); 173 } 174 175 /** 176 * Checks if the specified object exists in the form. 177 * 178 * @param name name to check 179 * @return boolean 180 */ 181 public boolean hasElement(String name) { 182 return elements(name) != null; 183 } 184 185 /* 186 public Label getLabel(String name) { 187 return (Label) elements(name); 188 } 189 190 public Button getButton(String name) { 191 return (Button) elements(name); 192 } 193 */ 194 195 /** 196 * Poplates the form objects based on the supplied java bean. 197 * 198 * @param bean any object which can be examined by BeanUtils. 199 * @throws FormPolulateException if BeanUtils fails. 200 */ 201 public void populate(Object bean) throws FormPolulateException { 202 203 Map<String, String> map = null; 204 //@todo should this support nested properties like the grid? 205 try { 206 map = BeanUtils.describe(bean); 207 } catch (Exception e) { 208 throw new FormPolulateException(e); 209 } 210 populate(map); 211 } 212 213 /** 214 * Populates the form object from the supplied map object. 215 * 216 * @param map - name/values where names match element names 217 */ 218 public void populate(Map<String, String> map) { 219 Map<String, String> radios = new HashMap<String, String>(3); 220 221 Iterator it = map.keySet().iterator(); 222 while (it.hasNext()) { 223 String key = (String) it.next(); 224 225 // Prevent null values in the bean from throwing null pointer 226 String value; 227 if (map.get(key) == null) { 228 value = ""; 229 } else { 230 value = (String) map.get(key); 231 } 232 233 if (hasElement(key.toString())) { 234 // Do not auto populate Radios since we don't really 235 // know which radio to assign the value to since usually 236 // they would have the same name. See getRadioGroup() method. 237 if (elements(key) instanceof Radio) { 238 radios.put(key, ""); 239 } else { 240 if (elements(key) instanceof CheckBox) { 241 if (value.equalsIgnoreCase("true")) { 242 elements(key).setChecked(true); 243 } else { 244 elements(key).setChecked(false); 245 } 246 } else { 247 elements(key).setValue(value); 248 } 249 } 250 } 251 } 252 253 /* 254 Check radios. If there are any radios that matched a field in the bean 255 then it means that maybe that fields value matches a radio value from the 256 group. For each group check if the values match and set the CHECKED for 257 the ones that do. 258 */ 259 it = radios.keySet().iterator(); 260 while (it.hasNext()) { 261 String key; 262 key = it.next().toString(); 263 264 Iterator<HTMLElement> itGroup; 265 itGroup = getRadioGroup(key).values().iterator(); 266 while (itGroup.hasNext()) { 267 HTMLElement radio = itGroup.next(); 268 radio.setChecked(false); 269 if (radio.getValue() != null && map.get(key) != null) { 270 if (radio.getValue().equalsIgnoreCase(map.get(key).toString())) { 271 radio.setChecked(true); 272 } 273 } 274 } 275 } 276 277 } 278 279 final HTMLElement addObject(Elements type, Attributes attributes, int start, int end) { 280 281 HTMLElement element; 282 283 log.debug("TYPE:" + type); 284 285 switch (type) { 286 287 case select: 288 element = new Select(); 289 break; 290 291 case textarea: 292 element = new TextArea(); 293 break; 294 295 case label: 296 element = new Label(); 297 break; 298 299 case button: 300 element = new Button(); 301 break; 302 303 case image: 304 element = new Image(); 305 break; 306 307 case grid: 308 element = new Grid(); 309 break; 310 311 case radio: 312 element = new Radio(); 313 break; 314 315 case checkbox: 316 element = new CheckBox(); 317 break; 318 319 case text: 320 element = new Text(); 321 break; 322 323 default: 324 element = new Input(); 325 } 326 327 log.debug("CLASS:" + element.getClass().getName()); 328 329 Iterator i = attributes.keySet().iterator(); 330 331 while (i.hasNext()) { 332 String key, value; 333 key = (String) i.next(); 334 value = attributes.getAttribute(key); 335 element.setAttribute(key, value); 336 337 // Ensure to call setValue for values. 338 if ("value".equalsIgnoreCase(key)) { 339 element.setValue(value); 340 } 341 } 342 343 // Also assign the object's name if found. 344 if (attributes.hasAttribute("name")) { 345 element.setName(attributes.getAttribute("name")); 346 } else if (attributes.hasAttribute("id")) { 347 element.setName(attributes.getAttribute("id")); 348 } 349 350 element.setStartPoint(start); 351 element.setEndPoint(end); 352 353 elements.add(element); 354 return element; 355 } 356 357 /** 358 * Used for cloning. 359 * 360 * @param element to add 361 * @return HTMLElement 362 */ 363 HTMLElement addElement(HTMLElement element) { 364 elements.add(element); 365 return element; 366 } 367 368 369 }