
net.sf.wcfart.wcf.form.FormComponent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wcf-art Show documentation
Show all versions of wcf-art Show documentation
jpivot-wcf code used by the ART reporting tool
The newest version!
/*
* ====================================================================
* This software is subject to the terms of the Common Public License
* Agreement, available at the following URL:
* http://www.opensource.org/licenses/cpl.html .
* Copyright (C) 2003-2004 TONBELLER AG.
* All Rights Reserved.
* You must accept the terms of that agreement to use this software.
* ====================================================================
*
*
*/
package net.sf.wcfart.wcf.form;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.jaxen.JaxenException;
import org.jaxen.dom.DOMXPath;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import net.sf.wcfart.wcf.component.Component;
import net.sf.wcfart.wcf.component.FormListener;
import net.sf.wcfart.wcf.controller.RequestContext;
import net.sf.wcfart.wcf.controller.RequestListener;
import net.sf.wcfart.wcf.convert.ConvertException;
import net.sf.wcfart.wcf.format.FormatException;
import net.sf.wcfart.wcf.ui.XoplonCtrl;
import net.sf.wcfart.wcf.utils.DomUtils;
import net.sf.wcfart.wcf.utils.IdGenerator;
import net.sf.wcfart.wcf.utils.NoWarn;
import net.sf.wcfart.wcf.utils.SoftException;
import net.sf.wcfart.wcf.utils.XoplonNS;
import net.sf.wcfart.wcf.wizard.PageListener;
import net.sf.wcfart.wcf.wizard.WizardPage;
import net.sf.wcfart.wcf.wizard.WizardPageSupport;
/**
* Manges a DOM that contains xoplon controls like <textField> etc
* @author av
*/
public class FormComponent extends XmlComponent implements FormListener, WizardPage {
private boolean dirty = true;
boolean haveError = false;
Object bean;
boolean bookmarkable = false;
boolean finishButton = true;
WizardPageSupport wizardPageSupport = new WizardPageSupport(this);
private static Logger logger = Logger.getLogger(FormComponent.class);
/**
* creates a FormComponent with beanModel == this
* @param id id
* @param parent parent
* @param document document
*/
public FormComponent(String id, Component parent, Document document) {
this(id, parent, document, null);
this.bean = this;
}
/**
* creates a form component
*
* @param id id
* @param parent parent
* @param document document
* @param bean bean
*/
public FormComponent(String id, Component parent, Document document, Object bean) {
super(id, parent, document);
this.bean = bean;
Element rootElem = document.getDocumentElement();
if (rootElem.getAttribute("id").length() == 0)
rootElem.setAttribute("id", id);
new IdGenerator().generate(document, id + ".");
}
/**
* fills the form with values from the bean
*/
public void revert(RequestContext context) {
super.revert(context);
try {
clearErrors();
context.getConverter().revert(bean, getDocument());
dirty = false;
haveError = false;
} catch (ConvertException e) {
logger.error("exception caught", e);
throw new SoftException(e);
}
}
/**
* fills bean and form from user input
*/
public boolean validate(RequestContext context) {
boolean success = super.validate(context);
try {
logger.info("enter");
context.getConverter().validate(context.getParameters(), context.getFileParameters(),
getDocument(), bean);
return success;
} catch (ConvertException e) {
logger.error(null, e);
throw new SoftException(e);
} catch (FormatException e) {
logger.info("invalid user input: " + e.getMessage());
haveError = true;
return false;
}
}
/**
* deferred ctor
* check if the FormListener is removed from environment when this is removed from session
* @param context context
*/
public void initialize(RequestContext context) throws Exception {
super.initialize(context);
initActionReferences();
dirty = true;
if (bean instanceof FormBean)
((FormBean) bean).setFormComponent(context, this);
}
/**
* if there is no error, the form is filled with the current model values.
* After that, the form is rendered.
* @param context context
* @return document
*/
public Document render(RequestContext context) throws Exception {
if (dirty || !haveError)
revert(context);
return super.render(context);
}
/**
* Sets the bean.
* @param bean The bean to set
*/
public void setBean(Object bean) {
this.bean = bean;
this.dirty = true;
}
/**
* Returns the beanModel.
* @return Object
*/
public Object getBean() {
return bean;
}
/**
* sets an error message to a DOM element
*
* @param id the value of the id
attribute of the
* DOM Element that the error message will be attached to
*
* @param message the error message or null to remove the error attribute
*/
public void setError(String id, String message) {
Element elem = DomUtils.findElementWithId(id, getDocument().getDocumentElement());
if (elem == null) {
logger.error("No errorElement found with id=" + id + " in XML form");
return;
}
if (message == null)
DomUtils.removeAttribute(elem, "error");
else
elem.setAttribute("error", message);
haveError = true;
}
/**
* clears all error attributes in the DOM
*/
public void clearErrors() {
clearErrors(getDocument().getChildNodes());
haveError = false;
}
protected void clearErrors(NodeList list) {
int len = list.getLength();
for (int i = 0; i < len; i++) {
Node n = list.item(i);
if (n.getNodeType() != Node.ELEMENT_NODE)
continue;
Element x = (Element) list.item(i);
DomUtils.removeAttribute(x, "error");
clearErrors(x.getChildNodes());
}
}
/**
* handler for an actionReference (e.g. a Button).
*/
private class ActionReferenceListener implements RequestListener {
private Element elem;
private String methodPath;
public ActionReferenceListener(Element elem, String methodPath) {
this.elem = elem;
this.methodPath = methodPath;
}
public void request(RequestContext context) throws Exception {
// validate or revert?
String action = elem.getAttribute("action");
if (action.equals("revert"))
revert(context);
else if (action.equals("validate")) {
if (!validate(context))
return;
}
// to be compatible with previous WCF versions the state must be modified
// before the actionReference is invoked.
Object[] state = performButtonActions(context);
if (!invokeActionReference(context)) {
restoreButtonActions(context, state);
return;
}
wizardPageSupport.fireWizardButton(context, methodName());
}
private String methodName() {
String methodName = methodPath;
int index = methodPath.lastIndexOf('.');
if (index > 0)
methodName = methodPath.substring(index + 1);
return methodName;
}
private Object beanTarget() throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Object target = bean;
String methodName = methodPath;
int index = methodPath.lastIndexOf('.');
if (index > 0) {
String beanPath = methodPath.substring(0, index);
target = PropertyUtils.getProperty(target, beanPath);
}
return target;
}
/**
* assumes that the actionReference will not throw an ActionReferenceException
*/
private Object[] performButtonActions(RequestContext context) {
Object[] state = new Object[2];
// hide?
state[0] = Boolean.valueOf(isVisible());
if ("true".equals(elem.getAttribute("hide")))
setVisible(false);
// forward?
state[1] = getNextView();
String forward = elem.getAttribute("forward");
if (forward != null && forward.length() > 0)
setNextView(forward);
// set attribute?
String successAttr = elem.getAttribute("successAttr");
if (successAttr.length() > 0)
context.getRequest().setAttribute(successAttr, "true");
return state;
}
/**
* restores state after actionRefrence threw an ActionReferenceException
*/
private void restoreButtonActions(RequestContext context, Object[] state) {
setVisible(((Boolean) state[0]).booleanValue());
setNextView((String) state[1]);
String successAttr = elem.getAttribute("successAttr");
if (successAttr.length() > 0)
context.getRequest().removeAttribute(successAttr);
}
/**
* invokes the actionReference on the bean.
* @return false, if the actionReference threw an ActionReferenceException
*/
private boolean invokeActionReference(RequestContext context) throws IllegalAccessException,
InvocationTargetException {
if (bean == null)
return true;
try {
Object target = beanTarget();
String methodName = methodName();
Class> c = target.getClass();
Method m = c.getMethod(methodName, new Class>[] { RequestContext.class});
m.invoke(target, new Object[] { context});
return true;
} catch (NoSuchMethodException e) {
logger.error("method not found: " + methodPath + " in " + bean);
return true;
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof ActionReferenceException)
return false;
throw e;
}
}
}
/**
* finds all DOM elements with an actionReference attribute
* and adds a Requesthandler for these.
*/
private void initActionReferences() {
try {
DOMXPath dx = new DOMXPath("//*[@actionReference]");
List elements = NoWarn.castList(dx.selectNodes(getDocument()));
for (Element elem : elements) {
String methodPath = elem.getAttribute("actionReference");
if ("none".equals(methodPath))
continue;
RequestListener rl = new ActionReferenceListener(elem, methodPath);
String id = elem.getAttribute("id");
getDispatcher().addRequestListener(id, null, rl);
}
} catch (JaxenException e) {
logger.error(null, e);
}
}
/**
* adds all editable properties to the bookmark state. Editable
* properties are addressed via the modelReference
* attribute in the DOM.
* @param levelOfDetail level of detail
* @return map
*/
public Object retrieveBookmarkState(int levelOfDetail) {
if (!bookmarkable)
return null;
@SuppressWarnings("unchecked")
Map map = (Map) super.retrieveBookmarkState(levelOfDetail);
try {
DOMXPath dx = new DOMXPath("//*[@modelReference]");
List elements = NoWarn.castList(dx.selectNodes(getDocument()));
for (Element elem : elements) {
if ("false".equals(elem.getAttribute("bookmark")))
continue;
String ref = XoplonCtrl.getModelReference(elem);
Object value = PropertyUtils.getProperty(bean, ref);
map.put(ref, value);
}
} catch (JaxenException e) {
logger.error("?", e);
} catch (IllegalAccessException e) {
logger.error("?", e);
} catch (InvocationTargetException e) {
logger.error("?", e);
} catch (NoSuchMethodException e) {
logger.error("?", e);
}
return map;
}
/**
* restores all editable properties from the bookmark state. Editable
* properties are addressed via the modelReference
* attribute in the DOM.
*/
public void setBookmarkState(Object state) {
if (!bookmarkable)
return;
if (!(state instanceof Map))
return;
super.setBookmarkState(state);
@SuppressWarnings("unchecked")
Map map = (Map) state;
try {
DOMXPath dx = new DOMXPath("//*[@modelReference]");
List elements = NoWarn.castList(dx.selectNodes(getDocument()));
for (Element elem : elements) {
if ("false".equals(elem.getAttribute("bookmark")))
continue;
// Properties may be added and removed over time.
// So we react gentle if setting of a property fails
try {
String ref = XoplonCtrl.getModelReference(elem);
Object value = map.get(ref);
if (value != null)
PropertyUtils.setProperty(bean, ref, value);
} catch (IllegalAccessException e1) {
logger.error(null, e1);
} catch (InvocationTargetException e1) {
logger.error(null, e1);
logger.error(null, e1.getTargetException());
} catch (NoSuchMethodException e1) {
logger.warn(null, e1);
}
}
} catch (JaxenException e) {
logger.error(null, e);
}
}
/**
* @return is bookmarkable
*/
public boolean isBookmarkable() {
return bookmarkable;
}
/**
* @param b bookmarkable
*/
public void setBookmarkable(boolean b) {
bookmarkable = b;
}
public void addPageListener(PageListener l) {
wizardPageSupport.addPageListener(l);
}
public void removePageListener(PageListener l) {
wizardPageSupport.removePageListener(l);
}
/**
* shows/hides form buttons automatically depending on the position of
* this form in a wizard.
* This method sets the buttons with the following ids:
* $id.back, $id.next, $id.cancel, $id.finish, $id.ok.
*
* - Only page: ok/cancel
*
- First page: next/cancel/finish
*
- Intermediate page: back/next/cancel/finish
*
- Last page: back/cancel/finish
*
* Finish button is only displayed if the form component
* supports this: supportsPrematureFinishBtn()
* @param pagePos page position
*/
public void pageAdded(WizardPagePosition pagePos) {
boolean isBack = false;
boolean isNext = false;
boolean isFinish = false;
boolean isOk = false;
boolean isCancel = true;
if (pagePos == WizardPagePosition.FIRST_PAGE) {
isNext = true;
isFinish = isFinishButton();
} else if (pagePos == WizardPagePosition.MIDDLE_PAGE) {
isNext = true;
isBack = true;
isFinish = isFinishButton();
} else if (pagePos == WizardPagePosition.LAST_PAGE) {
isNext = false;
isBack = true;
isFinish = true;
} else if (pagePos == WizardPagePosition.SINGLE_PAGE) {
isOk = true;
}
showButton("back", isBack);
showButton("next", isNext);
showButton("finish", isFinish);
showButton("ok", isOk);
showButton("cancel", isCancel);
}
private void showButton(String name, boolean show) {
Element btn = getElement(getId() + "." + name);
if (btn == null)
return;
if(XoplonNS.getAttribute(btn, "hidden")!=null)
XoplonNS.removeAttribute(btn, "hidden");
if (!show)
XoplonNS.setAttribute(btn, "hidden", "true");
}
/**
* is finish button
* @return is finish button
*/
public boolean isFinishButton() {
return finishButton;
}
/**
* is finish button
* @param finishButton is finish button
*/
public void setFinishButton(boolean finishButton) {
this.finishButton = finishButton;
}
/**
* page skipped
*/
public void pageSkipped() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy