KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > wcf > form > FormComponent


1 /*
2  * ====================================================================
3  * This software is subject to the terms of the Common Public License
4  * Agreement, available at the following URL:
5  * http://www.opensource.org/licenses/cpl.html .
6  * Copyright (C) 2003-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.wcf.form;
14
15 import java.lang.reflect.InvocationTargetException JavaDoc;
16 import java.lang.reflect.Method JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.Map JavaDoc;
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 JavaDoc;
25 import org.w3c.dom.Element JavaDoc;
26 import org.w3c.dom.Node JavaDoc;
27 import org.w3c.dom.NodeList JavaDoc;
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 /**
45  * Manges a DOM that contains xoplon controls like <textField> etc
46  * @author av
47  */

48 public class FormComponent extends XmlComponent implements FormListener, WizardPage {
49   private boolean dirty = true;
50   boolean haveError = false;
51   Object JavaDoc 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   /**
59    * creates a FormComponent with beanModel == this
60    * @param id
61    * @param document
62    */

63   public FormComponent(String JavaDoc id, Component parent, Document JavaDoc document) {
64     this(id, parent, document, null);
65     this.bean = this;
66   }
67
68   public FormComponent(String JavaDoc id, Component parent, Document JavaDoc document, Object JavaDoc bean) {
69     super(id, parent, document);
70     this.bean = bean;
71     Element JavaDoc rootElem = document.getDocumentElement();
72     if (rootElem.getAttribute("id").length() == 0)
73       rootElem.setAttribute("id", id);
74     new IdGenerator().generate(document, id + ".");
75   }
76
77   /**
78    * fills the form with values from the bean
79    */

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   /**
94    * fills bean and form from user input
95    */

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   /**
114    * deferred ctor
115    * @task check if the FormListener is removed from environment when this is removed from session
116    */

117   public void initialize(RequestContext context) throws Exception JavaDoc {
118     super.initialize(context);
119     initActionReferences();
120     dirty = true;
121     if (bean instanceof FormBean)
122       ((FormBean) bean).setFormComponent(context, this);
123   }
124
125   /**
126    * if there is no error, the form is filled with the current model values.
127    * After that, the form is rendered.
128    */

129   public Document JavaDoc render(RequestContext context) throws Exception JavaDoc {
130     if (dirty || !haveError)
131       revert(context);
132     return super.render(context);
133   }
134
135   /**
136    * Sets the beanModel.
137    * @param beanModel The beanModel to set
138    */

139   public void setBean(Object JavaDoc bean) {
140     this.bean = bean;
141     this.dirty = true;
142   }
143
144   /**
145    * Returns the beanModel.
146    * @return Object
147    */

148   public Object JavaDoc getBean() {
149     return bean;
150   }
151
152   /**
153    * sets an error message to a DOM element
154    *
155    * @param id the value of the <code>id</code> attribute of the
156    * DOM Element that the error message will be attached to
157    *
158    * @param message the error message or null to remove the error attribute
159    */

160   public void setError(String JavaDoc id, String JavaDoc message) {
161     Element JavaDoc 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   /**
174    * clears all error attributes in the DOM
175    */

176   public void clearErrors() {
177     clearErrors(getDocument().getChildNodes());
178     haveError = false;
179   }
180
181   protected void clearErrors(NodeList JavaDoc list) {
182     int len = list.getLength();
183     for (int i = 0; i < len; i++) {
184       Node JavaDoc n = list.item(i);
185       if (n.getNodeType() != Node.ELEMENT_NODE)
186         continue;
187       Element JavaDoc x = (Element JavaDoc) list.item(i);
188       DomUtils.removeAttribute(x, "error");
189       clearErrors(x.getChildNodes());
190     }
191   }
192
193   /**
194    * handler for an actionReference (e.g. a Button).
195    */

196   private class ActionReferenceListener implements RequestListener {
197     private Element JavaDoc elem;
198     private String JavaDoc methodPath;
199
200     public ActionReferenceListener(Element JavaDoc elem, String JavaDoc methodPath) {
201       this.elem = elem;
202       this.methodPath = methodPath;
203     }
204
205     public void request(RequestContext context) throws Exception JavaDoc {
206       // validate or revert?
207
String JavaDoc 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       // to be compatible with previous WCF versions the state must be modified
216
// before the actionReference is invoked.
217
Object JavaDoc[] 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 JavaDoc methodName() {
227       String JavaDoc 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 JavaDoc beanTarget() throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc,
235         NoSuchMethodException JavaDoc {
236       Object JavaDoc target = bean;
237       String JavaDoc methodName = methodPath;
238       int index = methodPath.lastIndexOf('.');
239       if (index > 0) {
240         String JavaDoc beanPath = methodPath.substring(0, index);
241         target = PropertyUtils.getProperty(target, beanPath);
242       }
243       return target;
244     }
245
246     /**
247      * assumes that the actionReference will not throw an ActionReferenceException
248      */

249     private Object JavaDoc[] performButtonActions(RequestContext context) {
250       Object JavaDoc[] state = new Object JavaDoc[2];
251       // hide?
252
state[0] = Boolean.valueOf(isVisible());
253       if ("true".equals(elem.getAttribute("hide")))
254         setVisible(false);
255       // forward?
256
state[1] = getNextView();
257       String JavaDoc forward = elem.getAttribute("forward");
258       if (forward != null && forward.length() > 0)
259         setNextView(forward);
260       // set attribute?
261
String JavaDoc successAttr = elem.getAttribute("successAttr");
262       if (successAttr.length() > 0)
263         context.getRequest().setAttribute(successAttr, "true");
264       return state;
265     }
266
267     /**
268      * restores state after actionRefrence threw an ActionReferenceException
269      */

270     private void restoreButtonActions(RequestContext context, Object JavaDoc[] state) {
271       setVisible(((Boolean JavaDoc) state[0]).booleanValue());
272       setNextView((String JavaDoc) state[1]);
273       String JavaDoc successAttr = elem.getAttribute("successAttr");
274       if (successAttr.length() > 0)
275         context.getRequest().removeAttribute(successAttr);
276     }
277
278     /**
279      * invokes the actionReference on the bean.
280      * @return false, if the actionReference threw an ActionReferenceException
281      */

282     private boolean invokeActionReference(RequestContext context) throws IllegalAccessException JavaDoc,
283         InvocationTargetException JavaDoc {
284       if (bean == null)
285         return true;
286       try {
287         Object JavaDoc target = beanTarget();
288         String JavaDoc methodName = methodName();
289         Class JavaDoc c = target.getClass();
290         Method JavaDoc m = c.getMethod(methodName, new Class JavaDoc[] { RequestContext.class});
291         m.invoke(target, new Object JavaDoc[] { context});
292         return true;
293       } catch (NoSuchMethodException JavaDoc e) {
294         logger.error("method not found: " + methodPath + " in " + bean);
295         return true;
296       } catch (InvocationTargetException JavaDoc e) {
297         Throwable JavaDoc t = e.getTargetException();
298         if (t instanceof ActionReferenceException)
299           return false;
300         throw e;
301       }
302     }
303   }
304
305   /**
306    * finds all DOM elements with an <tt>actionReference</tt> attribute
307    * and adds a Requesthandler for these.
308    */

309   private void initActionReferences() {
310     try {
311       DOMXPath dx = new DOMXPath("//*[@actionReference]");
312       for (Iterator JavaDoc it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) {
313         Element JavaDoc elem = (Element JavaDoc) it.next();
314         String JavaDoc methodPath = elem.getAttribute("actionReference");
315         if ("none".equals(methodPath))
316           continue;
317         RequestListener rl = new ActionReferenceListener(elem, methodPath);
318         String JavaDoc id = elem.getAttribute("id");
319         getDispatcher().addRequestListener(id, null, rl);
320       }
321     } catch (JaxenException e) {
322       logger.error(null, e);
323     }
324   }
325
326   /**
327    * adds all editable properties to the bookmark state. Editable
328    * properties are addressed via the <code>modelReference</code>
329    * attribute in the DOM.
330    */

331   public Object JavaDoc getBookmarkState(int levelOfDetail) {
332     if (!bookmarkable)
333       return null;
334     Map JavaDoc map = (Map JavaDoc) super.getBookmarkState(levelOfDetail);
335     try {
336       DOMXPath dx = new DOMXPath("//*[@modelReference]");
337       for (Iterator JavaDoc it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) {
338         Element JavaDoc elem = (Element JavaDoc) it.next();
339         if ("false".equals(elem.getAttribute("bookmark")))
340           continue;
341         String JavaDoc ref = XoplonCtrl.getModelReference(elem);
342         Object JavaDoc value = PropertyUtils.getProperty(bean, ref);
343         map.put(ref, value);
344       }
345     } catch (JaxenException e) {
346       logger.error("?", e);
347     } catch (IllegalAccessException JavaDoc e) {
348       logger.error("?", e);
349     } catch (InvocationTargetException JavaDoc e) {
350       logger.error("?", e);
351     } catch (NoSuchMethodException JavaDoc e) {
352       logger.error("?", e);
353     }
354     return map;
355   }
356
357   /**
358    * restores all editable properties from the bookmark state. Editable
359    * properties are addressed via the <code>modelReference</code>
360    * attribute in the DOM.
361    */

362   public void setBookmarkState(Object JavaDoc state) {
363     if (!bookmarkable)
364       return;
365     if (!(state instanceof Map JavaDoc))
366       return;
367     super.setBookmarkState(state);
368     Map JavaDoc map = (Map JavaDoc) state;
369     try {
370       DOMXPath dx = new DOMXPath("//*[@modelReference]");
371       for (Iterator JavaDoc it = dx.selectNodes(getDocument()).iterator(); it.hasNext();) {
372         Element JavaDoc elem = (Element JavaDoc) it.next();
373         if ("false".equals(elem.getAttribute("bookmark")))
374           continue;
375         // Properties may be added and removed over time.
376
// So we react gentle if setting of a property fails
377
try {
378           String JavaDoc ref = XoplonCtrl.getModelReference(elem);
379           Object JavaDoc value = map.get(ref);
380           if (value != null)
381             PropertyUtils.setProperty(bean, ref, value);
382         } catch (IllegalAccessException JavaDoc e1) {
383           logger.error(null, e1);
384         } catch (InvocationTargetException JavaDoc e1) {
385           logger.error(null, e1);
386           logger.error(null, e1.getTargetException());
387         } catch (NoSuchMethodException JavaDoc e1) {
388           logger.warn(null, e1);
389         }
390       }
391     } catch (JaxenException e) {
392       logger.error(null, e);
393     }
394   }
395
396   /**
397    * @return
398    */

399   public boolean isBookmarkable() {
400     return bookmarkable;
401   }
402
403   /**
404    * @param b
405    */

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   /**
419    * shows/hides form buttons automatically depending on the position of
420    * this form in a wizard.
421    * This method sets the buttons with the following ids:
422    * $id.back, $id.next, $id.cancel, $id.finish, $id.ok.
423    * <ul>
424    * <li>Only page: ok/cancel
425    * <li>First page: next/cancel/finish
426    * <li>Intermediate page: back/next/cancel/finish
427    * <li>Last page: back/cancel/finish
428    * </ul>
429    * Finish button is only displayed if the form component
430    * supports this: supportsPrematureFinishBtn()
431    */

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 JavaDoc name, boolean show) {
463     Element JavaDoc 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