1 13 package com.tonbeller.wcf.form; 14 15 import java.lang.reflect.InvocationTargetException ; 16 import java.lang.reflect.Method ; 17 import java.util.Iterator ; 18 import java.util.Map ; 19 20 import org.apache.commons.beanutils.PropertyUtils; 21 import org.apache.log4j.Logger; 22 import org.jaxen.JaxenException; 23 import org.jaxen.dom.DOMXPath; 24 import org.w3c.dom.Document ; 25 import org.w3c.dom.Element ; 26 import org.w3c.dom.Node ; 27 import org.w3c.dom.NodeList ; 28 29 import com.tonbeller.wcf.component.Component; 30 import com.tonbeller.wcf.component.FormListener; 31 import com.tonbeller.wcf.controller.RequestContext; 32 import com.tonbeller.wcf.controller.RequestListener; 33 import com.tonbeller.wcf.convert.ConvertException; 34 import com.tonbeller.wcf.format.FormatException; 35 import com.tonbeller.wcf.ui.XoplonCtrl; 36 import com.tonbeller.wcf.utils.DomUtils; 37 import com.tonbeller.wcf.utils.IdGenerator; 38 import com.tonbeller.wcf.utils.SoftException; 39 import com.tonbeller.wcf.utils.XoplonNS; 40 import com.tonbeller.wcf.wizard.PageListener; 41 import com.tonbeller.wcf.wizard.WizardPage; 42 import com.tonbeller.wcf.wizard.WizardPageSupport; 43 44 48 public class FormComponent extends XmlComponent implements FormListener, WizardPage { 49 private boolean dirty = true; 50 boolean haveError = false; 51 Object bean; 52 boolean bookmarkable = false; 53 boolean finishButton = true; 54 WizardPageSupport wizardPageSupport = new WizardPageSupport(this); 55 56 private static Logger logger = Logger.getLogger(FormComponent.class); 57 58 63 public FormComponent(String id, Component parent, Document document) { 64 this(id, parent, document, null); 65 this.bean = this; 66 } 67 68 public FormComponent(String id, Component parent, Document document, Object bean) { 69 super(id, parent, document); 70 this.bean = bean; 71 Element rootElem = document.getDocumentElement(); 72 if (rootElem.getAttribute("id").length() == 0) 73 rootElem.setAttribute("id", id); 74 new IdGenerator().generate(document, id + "."); 75 } 76 77 80 public void revert(RequestContext context) { 81 super.revert(context); 82 try { 83 clearErrors(); 84 context.getConverter().revert(bean, getDocument()); 85 dirty = false; 86 haveError = false; 87 } catch (ConvertException e) { 88 logger.error("exception caught", e); 89 throw new SoftException(e); 90 } 91 } 92 93 96 public boolean validate(RequestContext context) { 97 boolean success = super.validate(context); 98 try { 99 logger.info("enter"); 100 context.getConverter().validate(context.getParameters(), context.getFileParameters(), 101 getDocument(), bean); 102 return success; 103 } catch (ConvertException e) { 104 logger.error(null, e); 105 throw new SoftException(e); 106 } catch (FormatException e) { 107 logger.info("invalid user input: " + e.getMessage()); 108 haveError = true; 109 return false; 110 } 111 } 112 113 117 public void initialize(RequestContext context) throws Exception { 118 super.initialize(context); 119 initActionReferences(); 120 dirty = true; 121 if (bean instanceof FormBean) 122 ((FormBean) bean).setFormComponent(context, this); 123 } 124 125 129 public Document render(RequestContext context) throws Exception { 130 if (dirty || !haveError) 131 revert(context); 132 return super.render(context); 133 } 134 135 139 public void setBean(Object bean) { 140 this.bean = bean; 141 this.dirty = true; 142 } 143 144 148 public Object getBean() { 149 return bean; 150 } 151 152 160 public void setError(String id, String message) { 161 Element elem = DomUtils.findElementWithId(id, getDocument().getDocumentElement()); 162 if (elem == null) { 163 logger.error("No errorElement found with id=" + id + " in XML form"); 164 return; 165 } 166 if (message == null) 167 DomUtils.removeAttribute(elem, "error"); 168 else 169 elem.setAttribute("error", message); 170 haveError = true; 171 } 172 173 176 public void clearErrors() { 177 clearErrors(getDocument().getChildNodes()); 178 haveError = false; 179 } 180 181 protected void clearErrors(NodeList list) { 182 int len = list.getLength(); 183 for (int i = 0; i < len; i++) { 184 Node n = list.item(i); 185 if (n.getNodeType() != Node.ELEMENT_NODE) 186 continue; 187 Element x = (Element ) list.item(i); 188 DomUtils.removeAttribute(x, "error"); 189 clearErrors(x.getChildNodes()); 190 } 191 } 192 193 196 private class ActionReferenceListener implements RequestListener { 197 private Element elem; 198 private String methodPath; 199 200 public ActionReferenceListener(Element elem, String methodPath) { 201 this.elem = elem; 202 this.methodPath = methodPath; 203 } 204 205 public void request(RequestContext context) throws Exception { 206 String action = elem.getAttribute("action"); 208 if (action.equals("revert")) 209 revert(context); 210 else if (action.equals("validate")) { 211 if (!validate(context)) 212 return; 213 } 214 215 Object [] state = performButtonActions(context); 218 if (!invokeActionReference(context)) { 219 restoreButtonActions(context, state); 220 return; 221 } 222 223 wizardPageSupport.fireWizardButton(context, methodName()); 224 } 225 226 private String methodName() { 227 String methodName = methodPath; 228 int index = methodPath.lastIndexOf('.'); 229 if (index > 0) 230 methodName = methodPath.substring(index + 1); 231 return methodName; 232 } 233 234 private Object beanTarget() throws IllegalAccessException , InvocationTargetException , 235 NoSuchMethodException { 236 Object target = bean; 237 String methodName = methodPath; 238 int index = methodPath.lastIndexOf('.'); 239 if (index > 0) { 240 String beanPath = methodPath.substring(0, index); 241 target = PropertyUtils.getProperty(target, beanPath); 242 } 243 return target; 244 } 245 246 249 private Object [] performButtonActions(RequestContext context) { 250 Object [] state = new Object [2]; 251 state[0] = Boolean.valueOf(isVisible()); 253 if ("true".equals(elem.getAttribute("hide"))) 254 setVisible(false); 255 state[1] = getNextView(); 257 String forward = elem.getAttribute("forward"); 258 if (forward != null && forward.length() > 0) 259 setNextView(forward); 260 String successAttr = elem.getAttribute("successAttr"); 262 if (successAttr.length() > 0) 263 context.getRequest().setAttribute(successAttr, "true"); 264 return state; 265 } 266 267 270 private void restoreButtonActions(RequestContext context, Object [] state) { 271 setVisible(((Boolean ) state[0]).booleanValue()); 272 setNextView((String ) state[1]); 273 String successAttr = elem.getAttribute("successAttr"); 274 if (successAttr.length() > 0) 275 context.getRequest().removeAttribute(successAttr); 276 } 277 278 282 private boolean invokeActionReference(RequestContext context) throws IllegalAccessException , 283 InvocationTargetException { 284 if (bean == null) 285 return true; 286 try { 287 Object target = beanTarget(); 288 String methodName = methodName(); 289 Class c = target.getClass(); 290 Method m = c.getMethod(methodName, new Class [] { RequestContext.class}); 291 m.invoke(target, new Object [] { context}); 292 return true; 293 } catch (NoSuchMethodException e) { 294 logger.error("method not found: " + methodPath + " in " + bean); 295 return true; 296 } catch (InvocationTargetException e) { 297 Throwable t = e.getTargetException(); 298 if (t instanceof ActionReferenceException) 299 return false; 300 throw e; 301 } 302 } 303 } 304 305 309 private void initActionReferences() { 310 try { 311 DOMXPath dx = new DOMXPath("//*[@actionReference]"); 312 for (Iterator it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) { 313 Element elem = (Element ) it.next(); 314 String methodPath = elem.getAttribute("actionReference"); 315 if ("none".equals(methodPath)) 316 continue; 317 RequestListener rl = new ActionReferenceListener(elem, methodPath); 318 String id = elem.getAttribute("id"); 319 getDispatcher().addRequestListener(id, null, rl); 320 } 321 } catch (JaxenException e) { 322 logger.error(null, e); 323 } 324 } 325 326 331 public Object getBookmarkState(int levelOfDetail) { 332 if (!bookmarkable) 333 return null; 334 Map map = (Map ) super.getBookmarkState(levelOfDetail); 335 try { 336 DOMXPath dx = new DOMXPath("//*[@modelReference]"); 337 for (Iterator it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) { 338 Element elem = (Element ) it.next(); 339 if ("false".equals(elem.getAttribute("bookmark"))) 340 continue; 341 String ref = XoplonCtrl.getModelReference(elem); 342 Object value = PropertyUtils.getProperty(bean, ref); 343 map.put(ref, value); 344 } 345 } catch (JaxenException e) { 346 logger.error("?", e); 347 } catch (IllegalAccessException e) { 348 logger.error("?", e); 349 } catch (InvocationTargetException e) { 350 logger.error("?", e); 351 } catch (NoSuchMethodException e) { 352 logger.error("?", e); 353 } 354 return map; 355 } 356 357 362 public void setBookmarkState(Object state) { 363 if (!bookmarkable) 364 return; 365 if (!(state instanceof Map )) 366 return; 367 super.setBookmarkState(state); 368 Map map = (Map ) state; 369 try { 370 DOMXPath dx = new DOMXPath("//*[@modelReference]"); 371 for (Iterator it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) { 372 Element elem = (Element ) it.next(); 373 if ("false".equals(elem.getAttribute("bookmark"))) 374 continue; 375 try { 378 String ref = XoplonCtrl.getModelReference(elem); 379 Object value = map.get(ref); 380 if (value != null) 381 PropertyUtils.setProperty(bean, ref, value); 382 } catch (IllegalAccessException e1) { 383 logger.error(null, e1); 384 } catch (InvocationTargetException e1) { 385 logger.error(null, e1); 386 logger.error(null, e1.getTargetException()); 387 } catch (NoSuchMethodException e1) { 388 logger.warn(null, e1); 389 } 390 } 391 } catch (JaxenException e) { 392 logger.error(null, e); 393 } 394 } 395 396 399 public boolean isBookmarkable() { 400 return bookmarkable; 401 } 402 403 406 public void setBookmarkable(boolean b) { 407 bookmarkable = b; 408 } 409 410 public void addPageListener(PageListener l) { 411 wizardPageSupport.addPageListener(l); 412 } 413 414 public void removePageListener(PageListener l) { 415 wizardPageSupport.removePageListener(l); 416 } 417 418 432 public void pageAdded(WizardPagePosition pagePos) { 433 boolean isBack = false; 434 boolean isNext = false; 435 boolean isFinish = false; 436 boolean isOk = false; 437 438 boolean isCancel = true; 439 440 if (pagePos == WizardPagePosition.FIRST_PAGE) { 441 isNext = true; 442 isFinish = isFinishButton(); 443 } else if (pagePos == WizardPagePosition.MIDDLE_PAGE) { 444 isNext = true; 445 isBack = true; 446 isFinish = isFinishButton(); 447 } else if (pagePos == WizardPagePosition.LAST_PAGE) { 448 isNext = false; 449 isBack = true; 450 isFinish = true; 451 } else if (pagePos == WizardPagePosition.SINGLE_PAGE) { 452 isOk = true; 453 } 454 455 showButton("back", isBack); 456 showButton("next", isNext); 457 showButton("finish", isFinish); 458 showButton("ok", isOk); 459 showButton("cancel", isCancel); 460 } 461 462 private void showButton(String name, boolean show) { 463 Element btn = getElement(getId() + "." + name); 464 if (btn == null) 465 return; 466 467 if(XoplonNS.getAttribute(btn, "hidden")!=null) 468 XoplonNS.removeAttribute(btn, "hidden"); 469 470 if (!show) 471 XoplonNS.setAttribute(btn, "hidden", "true"); 472 } 473 474 public boolean isFinishButton() { 475 return finishButton; 476 } 477 478 public void setFinishButton(boolean finishButton) { 479 this.finishButton = finishButton; 480 } 481 482 public void pageSkipped() { 483 484 } 485 } | Popular Tags |