CCM NG:
- Fixed some errors
- Ported basic servlets and Bebop classes from old code
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3646 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
parent
5883127563
commit
da809a9fc5
|
|
@ -88,12 +88,25 @@
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-fileupload</groupId>
|
||||||
|
<artifactId>commons-fileupload</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>oro</groupId>
|
<groupId>oro</groupId>
|
||||||
<artifactId>oro</artifactId>
|
<artifactId>oro</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.jtidy</groupId>
|
||||||
|
<artifactId>jtidy</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.ChangeEvent;
|
||||||
|
import com.arsdigita.bebop.event.ChangeListener;
|
||||||
|
import com.arsdigita.bebop.event.EventListenerList;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.Lockable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A standard implementation of <code>SingleSelectionModel</code> and
|
||||||
|
* <code>Lockable</code>. Those wishing to define a SingleSelectionModel
|
||||||
|
* will ordinarily want to extend this class.
|
||||||
|
*
|
||||||
|
* @version $Id: AbstractSingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSingleSelectionModel
|
||||||
|
implements SingleSelectionModel, Lockable {
|
||||||
|
|
||||||
|
private EventListenerList m_listeners;
|
||||||
|
private boolean m_locked;
|
||||||
|
|
||||||
|
/** Creates a new AbstractSingleSelectionModel.
|
||||||
|
*/
|
||||||
|
public AbstractSingleSelectionModel() {
|
||||||
|
m_listeners = new EventListenerList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if there is a selected element.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return <code>true</code> if there is a selected component;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isSelected(PageState state) {
|
||||||
|
return getSelectedKey(state) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Object getSelectedKey(PageState state);
|
||||||
|
|
||||||
|
public abstract void setSelectedKey(PageState state, Object key);
|
||||||
|
|
||||||
|
public void clearSelection(PageState state) {
|
||||||
|
setSelectedKey(state, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection change events
|
||||||
|
|
||||||
|
public void addChangeListener(ChangeListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.add(ChangeListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChangeListener(ChangeListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(ChangeListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireStateChanged(PageState state) {
|
||||||
|
Iterator i = m_listeners.getListenerIterator(ChangeListener.class);
|
||||||
|
ChangeEvent e = null;
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
if ( e == null ) {
|
||||||
|
e = new ChangeEvent(this, state);
|
||||||
|
}
|
||||||
|
((ChangeListener) i.next()).stateChanged(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement Lockable
|
||||||
|
public void lock() {
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isLocked() {
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import java.util.TooManyListenersException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parent of all Bebop Link classes, this class represents a URL on a page.
|
||||||
|
* It may contain a label, an image, or any other component.
|
||||||
|
*
|
||||||
|
* <p> The following table lists all Bebop Link classes and suggests
|
||||||
|
* when they might be used.
|
||||||
|
* <p>
|
||||||
|
* <table BORDER=3>
|
||||||
|
* <tr>
|
||||||
|
* <th>Link Class</th>
|
||||||
|
* <th>Usage</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link BaseLink}</td>
|
||||||
|
* <td>Parent class of Bebop Link classes. Extend this class to
|
||||||
|
* build your own Link class.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link Link}</td>
|
||||||
|
* <td>Link class that manages its own URL variables. Session information
|
||||||
|
* is added to the target URL for this type.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link ExternalLink}</td>
|
||||||
|
* <td>Link that does not encode the URL with any session information.
|
||||||
|
* Used for a link to a page outside the site.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link ControlLink}</td>
|
||||||
|
* <td> Used for references within its own page (often
|
||||||
|
* as fields in a table header for sorting a column).</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link ActionLink}</td>
|
||||||
|
* <td>Sets its own control event and runs its own
|
||||||
|
* {@link com.arsdigita.bebop.event.ActionListener}s. When the link is clicked,
|
||||||
|
* the code in the Listener's <tt>actionPerformed</tt> method runs.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{@link ToggleLink}</td>
|
||||||
|
* <td>A link that turns into label when it is selected and
|
||||||
|
* turns back into a link when it is unselected.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* @version $Id: BaseLink.java 998 2005-11-15 22:27:13Z sskracic $
|
||||||
|
*/
|
||||||
|
public abstract class BaseLink extends DescriptiveComponent
|
||||||
|
implements Cloneable {
|
||||||
|
|
||||||
|
/** The name of the attribute used in XML to indicate which type of link
|
||||||
|
* this link represents. */
|
||||||
|
private final static String TYPE_ATTR = "type";
|
||||||
|
private final static String HREF_NO_JAVASCRIPT = "href_no_javascript";
|
||||||
|
private final static String HREF = "href";
|
||||||
|
|
||||||
|
/** Component used to display the link. Typically a Label, may be
|
||||||
|
* e.g. an image as well. */
|
||||||
|
protected Component m_child;
|
||||||
|
|
||||||
|
/** Property to store the url the Link points to. */
|
||||||
|
protected String m_url;
|
||||||
|
|
||||||
|
// Use the parent class' property!
|
||||||
|
// /** Property to store informational text for the user about the Link, e.g.
|
||||||
|
// * how to use it, or when to use it (or not to use it). */
|
||||||
|
// private GlobalizedMessage m_hint;
|
||||||
|
|
||||||
|
protected String m_noJavascriptURL = null;
|
||||||
|
|
||||||
|
private PrintListener m_printListener;
|
||||||
|
|
||||||
|
private String m_sConfirmMsg = "";
|
||||||
|
private GlobalizedMessage m_confirmMsg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor creates a link taking url as the target and display it to
|
||||||
|
* the user at the same time. It is the only allowed way to present the
|
||||||
|
* user with a not globlized information. The implementation currently
|
||||||
|
* miss-uses the Label component to display just a not globalized String
|
||||||
|
* which is deprecated.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @deprecated use BaseLink(Component,url) instead with a Label using a
|
||||||
|
* GlobalizedMessage instead
|
||||||
|
*/
|
||||||
|
public BaseLink(final String url) {
|
||||||
|
this(new Label(url), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param child display component (Label, Image, etc.)
|
||||||
|
* @param url URL to point at
|
||||||
|
*/
|
||||||
|
public BaseLink(final Component child, final String url) {
|
||||||
|
super();
|
||||||
|
m_url = url;
|
||||||
|
m_child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param child display component (Label, Image, etc.)
|
||||||
|
* @param listener PrintListener, may be used to change either the Display
|
||||||
|
* text or the url within a locked page.
|
||||||
|
*/
|
||||||
|
public BaseLink(final Component child, final PrintListener listener) {
|
||||||
|
this(child, "");
|
||||||
|
try {
|
||||||
|
addPrintListener(listener);
|
||||||
|
} catch (TooManyListenersException e) {
|
||||||
|
// Can't happen
|
||||||
|
throw new UncheckedWrapperException("Too many listeners: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public BaseLink(final PrintListener listener) {
|
||||||
|
this("", listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED constructors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param label as text
|
||||||
|
* @param url
|
||||||
|
* @deprecated use BaseLink(Component,url) instead with a Label using a
|
||||||
|
* GlobalizedMessage instead
|
||||||
|
*/
|
||||||
|
public BaseLink(final String label, final String url) {
|
||||||
|
this(new Label(label), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param label as text
|
||||||
|
* @param listener PrintListener, may be used to change either the Display
|
||||||
|
* text or the url within a locked page.
|
||||||
|
* @deprecated use BaseLink(Component,listener) instead with a Label using
|
||||||
|
* a GlobalizedMessage instead
|
||||||
|
*/
|
||||||
|
public BaseLink(final String label, final PrintListener listener) {
|
||||||
|
this(new Label(label), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone.
|
||||||
|
* @return
|
||||||
|
* @throws CloneNotSupportedException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
final BaseLink result = (BaseLink) super.clone();
|
||||||
|
result.m_printListener = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a print listener.
|
||||||
|
* Since the <code>PrintListener</code> is expected to modify the target
|
||||||
|
* of the <code>PrintEvent</code>, only one print listener can be set
|
||||||
|
* for a link.
|
||||||
|
*
|
||||||
|
* @param listener The print listener. Must not <code>null</code>.
|
||||||
|
* @throws IllegalArgumentException if <code>listener</code> is null.
|
||||||
|
* @throws TooManyListenersException if a print listener has previously been
|
||||||
|
* added.
|
||||||
|
*/
|
||||||
|
public void addPrintListener(final PrintListener listener)
|
||||||
|
throws IllegalStateException, TooManyListenersException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("Argument listener can not be null");
|
||||||
|
}
|
||||||
|
if (m_printListener != null) {
|
||||||
|
throw new TooManyListenersException("Too many listeners. Can only have one");
|
||||||
|
}
|
||||||
|
m_printListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously added print listener. If the passed in
|
||||||
|
* <code>listener</code> is not the listener that was added with
|
||||||
|
* {@link #addPrintListener addPrintListener}, an IllegalArgumentException
|
||||||
|
* will be thrown.
|
||||||
|
*
|
||||||
|
* @param listener The listener that was previously added with
|
||||||
|
* <code>addPrintListener</code>.
|
||||||
|
* Must not be <code>null</code>.
|
||||||
|
* @throws IllegalArgumentException if <code>listener</code> is not the
|
||||||
|
* currently registered print listener or is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public void removePrintListener(final PrintListener listener)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("listener can not be null");
|
||||||
|
}
|
||||||
|
if (listener != m_printListener) {
|
||||||
|
throw new IllegalArgumentException("listener is not registered with this widget");
|
||||||
|
}
|
||||||
|
m_printListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected BaseLink firePrintEvent(final PageState state) {
|
||||||
|
BaseLink l = this;
|
||||||
|
if (m_printListener != null) {
|
||||||
|
try {
|
||||||
|
l = (BaseLink) this.clone();
|
||||||
|
m_printListener.prepare(new PrintEvent(this, state, l));
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
l = this;
|
||||||
|
throw new UncheckedWrapperException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the label component used to display the Link. Typically a Label,
|
||||||
|
* but may be an other type, e.g. an Image, as well.
|
||||||
|
*
|
||||||
|
* @return Component used to display the Link.
|
||||||
|
*/
|
||||||
|
public final Component getChild() {
|
||||||
|
return m_child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChild(final Component child) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use a GlobalizedMessage to be used to display the link. It's primary
|
||||||
|
* purpose is to hide the parent class' method to prevent its usage because
|
||||||
|
* Labels and GlobalizedMessages are used here differently (a
|
||||||
|
* GlobalizedMessage is here not directly used as a Label by specifying it
|
||||||
|
* as an attribugte, inside a Label component).
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setLabel(final GlobalizedMessage message) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
Label label = new Label(message);
|
||||||
|
setChild( (Component)label);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final String getTarget() {
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setTarget(final String url) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
|
||||||
|
m_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type of link this link represents.
|
||||||
|
*
|
||||||
|
* @param type the type of link
|
||||||
|
*/
|
||||||
|
protected void setTypeAttr(final String type) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(TYPE_ATTR, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
protected abstract void generateURL(final PageState state, final Element parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Generates a DOM fragment:
|
||||||
|
* <p><pre>
|
||||||
|
* <bebop:link href="..." type="..." %bebopAttr;/>
|
||||||
|
* </pre>
|
||||||
|
* The <code>href</code> attribute contains the target the link should point
|
||||||
|
* to. The <code>type</code> attribute is used to give more fine grained
|
||||||
|
* control over which kind of link this element represents. The types are
|
||||||
|
* <code>link</code> for a <code>Link</code>, <code>control</code> for a
|
||||||
|
* {@link ControlLink}, and <code>toggle</code> for a {@link ToggleLink}.
|
||||||
|
* There may be additional attributes depending on what type of link this
|
||||||
|
* link represents.
|
||||||
|
*
|
||||||
|
* @see ControlLink#generateXML
|
||||||
|
* @see ToggleLink#generateXML
|
||||||
|
*
|
||||||
|
* @param state The current {@link PageState}.
|
||||||
|
* @param parent The XML element to attach the XML to.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(final PageState state, final Element parent) {
|
||||||
|
if (isVisible(state)) {
|
||||||
|
BaseLink target = firePrintEvent(state);
|
||||||
|
|
||||||
|
Element link = parent.newChildElement("bebop:link", BEBOP_XML_NS);
|
||||||
|
|
||||||
|
target.generateURL(state, link);
|
||||||
|
target.exportConfirmAttributes(state, link);
|
||||||
|
//setup the link without javascript
|
||||||
|
target.setupNoJavascriptURL(state, link);
|
||||||
|
target.exportAttributes(link);
|
||||||
|
target.generateExtraXMLAttributes(state, link);
|
||||||
|
target.generateDescriptionXML(state, link);
|
||||||
|
target.getChild().generateXML(state, link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param sUrl
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getAbsoluteUrl(final PageState state, final String sUrl) {
|
||||||
|
String sReturn = "";
|
||||||
|
|
||||||
|
if ((sUrl.indexOf(":") != -1) || sUrl.indexOf("/") == 0) {
|
||||||
|
//if sUrl contains a ":" or begins with a "/", then it is an absolute URL
|
||||||
|
sReturn = sUrl;
|
||||||
|
} else {
|
||||||
|
//otherwise, it is a relative URL, so we need to make it an absolute URL
|
||||||
|
|
||||||
|
//get the current URL
|
||||||
|
String sThisURL = "";
|
||||||
|
try {
|
||||||
|
sThisURL = state.stateAsURL();
|
||||||
|
} catch (java.io.IOException ioe) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
//trim the current URL back to the last "/" character
|
||||||
|
int iIndex = sThisURL.lastIndexOf("/");
|
||||||
|
|
||||||
|
//if there is no "/" character, then assume we are at server root
|
||||||
|
if (iIndex == -1) {
|
||||||
|
sReturn = "/" + sUrl;
|
||||||
|
} else {
|
||||||
|
sReturn = sThisURL.substring(0, iIndex) + "/" + sUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up no-JavaScript fallback HTML
|
||||||
|
*
|
||||||
|
* @param state The current {@link PageState}.
|
||||||
|
* @param link The link element.
|
||||||
|
*/
|
||||||
|
protected void setupNoJavascriptURL(final PageState state, final Element link) {
|
||||||
|
String sURL = null;
|
||||||
|
|
||||||
|
if (m_sConfirmMsg.length() > 0
|
||||||
|
|| (m_confirmMsg != null && m_confirmMsg.localize().toString().length() > 0)) {
|
||||||
|
|
||||||
|
//if we want the confirm link, create the link
|
||||||
|
String sOkUrl = getAbsoluteUrl(state, link.getAttribute(HREF));
|
||||||
|
String sCancelUrl = null;
|
||||||
|
try {
|
||||||
|
sCancelUrl = state.stateAsURL();
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
Assert.fail("Could not get current page state as URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_sConfirmMsg.length() > 0) {
|
||||||
|
sURL = ConfirmPage.getConfirmUrl(m_sConfirmMsg, sOkUrl, sCancelUrl);
|
||||||
|
} else if (m_confirmMsg != null) {
|
||||||
|
sURL = ConfirmPage.getConfirmUrl(m_confirmMsg.localize().toString(), sOkUrl, sCancelUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//don't want confirm link--just no javascript link
|
||||||
|
if (m_noJavascriptURL == null) {
|
||||||
|
//get the generatedURL and make it the default noJavaScript link
|
||||||
|
sURL = link.getAttribute(HREF);
|
||||||
|
} else {
|
||||||
|
sURL = m_noJavascriptURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
link.addAttribute(HREF_NO_JAVASCRIPT, sURL);
|
||||||
|
exportAttributes(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds type-specific XML attributes to the XML element representing
|
||||||
|
* this link. Subclasses should override this method if they introduce
|
||||||
|
* more attributes than the ones {@link #generateXML generateXML}
|
||||||
|
* produces by default.
|
||||||
|
*
|
||||||
|
* @param state The current request
|
||||||
|
* @param link The XML element representing this link
|
||||||
|
*/
|
||||||
|
protected void generateExtraXMLAttributes(final PageState state, final Element link) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets onClick event and <em>disables the javascript-based double-click
|
||||||
|
* protection for this link</em>. Not for confirmation messages; Should call
|
||||||
|
* setConfirmation for that.
|
||||||
|
*
|
||||||
|
* @param value The confirmation link. To not use the value {@code return confirm(} with this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @see #setConfirmation
|
||||||
|
*/
|
||||||
|
public void setOnClick(final String value) {
|
||||||
|
//should not use this method to set confirmation messages--should
|
||||||
|
//use setConfirmation() instead, or else the javascript will break
|
||||||
|
if (value != null) {
|
||||||
|
Assert.isTrue(!value.toLowerCase().startsWith("return confirm("),
|
||||||
|
"Do not use setOnClick() to set confirmation messages. "
|
||||||
|
+ "Use setCofirmation() instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttribute(ON_CLICK, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the user to click through a confirmation dialog before this link
|
||||||
|
* is followed. The user is prompted with the specified message. If the
|
||||||
|
* user does not does not confirm, the link is not followed. The current
|
||||||
|
* implementation uses the JavaScript confirm function and the onClick
|
||||||
|
* attribute.
|
||||||
|
* If JavaScript is not enabled in the client browser, this method will
|
||||||
|
* redirect the browser to a Bebop confirmation page rather than use
|
||||||
|
* a JavaScript confirmation.
|
||||||
|
* Subsequent calls to setOnClick will undo the effect of this method.
|
||||||
|
*
|
||||||
|
* @param message the confirmation message presented to the user. This
|
||||||
|
* message cannot have an apostrophe or back slash.
|
||||||
|
* @deprecated Use setConfirmation(final GlobalizedMessage msg) instead
|
||||||
|
*/
|
||||||
|
public void setConfirmation(final String message) {
|
||||||
|
//make sure that the message doesn't have any apostrophe's
|
||||||
|
//or back slashes
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
final boolean isGoodMessage = message.indexOf("'") == -1 && message.indexOf("\\") == -1;
|
||||||
|
Assert.isTrue(isGoodMessage,
|
||||||
|
"confirmation message cannot contain apostrophe or back slash");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sConfirmMsg = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a GlobalizedMessage as confirmation message
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
|
public void setConfirmation(final GlobalizedMessage msg) {
|
||||||
|
m_confirmMsg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate XML output for confirmation links
|
||||||
|
*
|
||||||
|
* @param state PageState
|
||||||
|
* @param link Parent element
|
||||||
|
*/
|
||||||
|
private void exportConfirmAttributes(final PageState state, final Element link) {
|
||||||
|
|
||||||
|
// If a confirmation message is set
|
||||||
|
if (m_sConfirmMsg.length() > 0 || m_confirmMsg != null) {
|
||||||
|
|
||||||
|
// then add the needed attributes to link
|
||||||
|
link.addAttribute("confirm", "confirm");
|
||||||
|
|
||||||
|
// If m_sConfirmMsg is not empty
|
||||||
|
if (m_sConfirmMsg.length() > 0) {
|
||||||
|
|
||||||
|
// then set the onclick attribute for the link with the static message
|
||||||
|
link.addAttribute(ON_CLICK, "return confirm(\\'" + m_sConfirmMsg + "\\');");
|
||||||
|
|
||||||
|
// else if m_configMsg is set
|
||||||
|
} else if (m_confirmMsg != null) {
|
||||||
|
|
||||||
|
//then set the onclick attribute for the link with a globalized message
|
||||||
|
link.addAttribute(ON_CLICK, "return confirm(\\'" + m_confirmMsg.localize() + "\\');");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setNoJavascriptTarget(final String sURL) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_noJavascriptURL = sURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getNoJavascriptTarget() {
|
||||||
|
return m_noJavascriptURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base page class for use with the PageFactory
|
||||||
|
* class. It sets two attributes on the XML tag for
|
||||||
|
* the bebop:page, namely <code>application</code>
|
||||||
|
* and <code>id</code>. The values for these two
|
||||||
|
* tags correspond to the parameters passed to the
|
||||||
|
* PageFactory.buildPage method.
|
||||||
|
* <p>
|
||||||
|
* This class is intended to be subclassed to provide
|
||||||
|
* the page infrastructure required by a project, for
|
||||||
|
* example, adding some navigation components.
|
||||||
|
* The SimplePage class provides a easy implementation
|
||||||
|
* whereby the navigation components can be specified
|
||||||
|
* in the enterprise.init file.
|
||||||
|
*/
|
||||||
|
public class BasePage extends Page {
|
||||||
|
|
||||||
|
public BasePage(String application,
|
||||||
|
Label title,
|
||||||
|
String id) {
|
||||||
|
super(title, new SimpleContainer());
|
||||||
|
|
||||||
|
Assert.exists(application, "application name");
|
||||||
|
setAttribute("application", application);
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
setAttribute("id", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Justin Ross
|
||||||
|
* @see com.arsdigita.bebop.BebopConfig
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public final class Bebop {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(Bebop.class);
|
||||||
|
|
||||||
|
private static BebopConfig s_config = BebopConfig.getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the <code>BebopConfig</code> object.
|
||||||
|
*/
|
||||||
|
public static BebopConfig getConfig() {
|
||||||
|
return s_config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.page.PageTransformer;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.runtime.AbstractConfig;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.ui.SimplePage;
|
||||||
|
import com.arsdigita.util.parameter.BooleanParameter;
|
||||||
|
import com.arsdigita.util.parameter.ClassParameter;
|
||||||
|
import com.arsdigita.util.parameter.EnumerationParameter;
|
||||||
|
import com.arsdigita.util.parameter.Parameter;
|
||||||
|
import com.arsdigita.util.parameter.SingletonParameter;
|
||||||
|
import com.arsdigita.util.parameter.StringParameter;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Justin Ross
|
||||||
|
* @see com.arsdigita.bebop.Bebop
|
||||||
|
* @version $Id: BebopConfig.java 1498 2007-03-19 16:22:15Z apevec $
|
||||||
|
*/
|
||||||
|
public final class BebopConfig extends AbstractConfig {
|
||||||
|
|
||||||
|
/** A logger instance to assist debugging. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(BebopConfig.class);
|
||||||
|
|
||||||
|
/** Singleton config object. */
|
||||||
|
private static BebopConfig s_config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gain a BebopConfig object.
|
||||||
|
*
|
||||||
|
* Singleton pattern, don't instantiate a config object using the
|
||||||
|
* constructor directly!
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static synchronized BebopConfig getInstance() {
|
||||||
|
if (s_config == null) {
|
||||||
|
s_config = new BebopConfig();
|
||||||
|
s_config.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set of configuration parameters
|
||||||
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* */
|
||||||
|
private final Parameter m_presenter = new SingletonParameter
|
||||||
|
("waf.bebop.presentation_manager", Parameter.REQUIRED,
|
||||||
|
new PageTransformer());
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private final Parameter m_page = new ClassParameter
|
||||||
|
("waf.bebop.base_page", Parameter.REQUIRED, SimplePage.class);
|
||||||
|
/** Pointer to JTidy validation listener config file */
|
||||||
|
private final Parameter m_tidy = new StringParameter
|
||||||
|
("waf.bebop.tidy_config_file", Parameter.REQUIRED,
|
||||||
|
"com/arsdigita/bebop/parameters/tidy.properties");
|
||||||
|
private final Parameter m_fancyErrors = new BooleanParameter
|
||||||
|
("waf.bebop.fancy_xsl_errors",
|
||||||
|
Parameter.REQUIRED,
|
||||||
|
Boolean.FALSE);
|
||||||
|
/** Double Click Protection, enabled by default for all buttons in a form.*/
|
||||||
|
private final Parameter m_dcpOnButtons = new BooleanParameter
|
||||||
|
("waf.bebop.dcp_on_buttons", Parameter.REQUIRED, Boolean.TRUE);
|
||||||
|
/** Double Click Protection, disabled by default for all links. */
|
||||||
|
private final Parameter m_dcpOnLinks = new BooleanParameter
|
||||||
|
("waf.bebop.dcp_on_links", Parameter.REQUIRED, Boolean.FALSE);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private final Parameter m_enableTreeSelect = new BooleanParameter
|
||||||
|
("waf.bebop.enable_tree_select_attribute",
|
||||||
|
Parameter.REQUIRED,
|
||||||
|
Boolean.FALSE);
|
||||||
|
/** List of supported DHTML editors, first one is default (Xinha) */
|
||||||
|
private final EnumerationParameter m_dhtmlEditor;
|
||||||
|
/** Path to DHTML editor source file, relativ to document root */
|
||||||
|
private final Parameter m_dhtmlEditorSrcFile;
|
||||||
|
/** */
|
||||||
|
private final Parameter m_showClassName = new BooleanParameter
|
||||||
|
("waf.bebop.show_class_name", Parameter.OPTIONAL, Boolean.FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Singelton pattern, don't instantiate a config object using the
|
||||||
|
* constructor directly! Use getConfig() instead.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BebopConfig() {
|
||||||
|
|
||||||
|
/** List of supported DHTML editors, first one is default (Xinha) */
|
||||||
|
m_dhtmlEditor = new EnumerationParameter("waf.bebop.dhtml_editor",
|
||||||
|
Parameter.REQUIRED,BebopConstants.BEBOP_XINHAEDITOR);
|
||||||
|
m_dhtmlEditor.put("Xinha", BebopConstants.BEBOP_XINHAEDITOR);
|
||||||
|
m_dhtmlEditor.put("FCKeditor", BebopConstants.BEBOP_FCKEDITOR);
|
||||||
|
// HTMLArea for backwards compatibility with old XSL. to be removed soon!
|
||||||
|
m_dhtmlEditor.put("HTMLArea", BebopConstants.BEBOP_DHTMLEDITOR);
|
||||||
|
|
||||||
|
// Xinha is now default!
|
||||||
|
m_dhtmlEditorSrcFile = new StringParameter
|
||||||
|
("waf.bebop.dhtml_editor_src", Parameter.REQUIRED,
|
||||||
|
"/assets/xinha/XinhaLoader.js");
|
||||||
|
|
||||||
|
register(m_presenter);
|
||||||
|
register(m_page);
|
||||||
|
register(m_tidy);
|
||||||
|
register(m_fancyErrors);
|
||||||
|
register(m_dhtmlEditor);
|
||||||
|
register(m_dhtmlEditorSrcFile);
|
||||||
|
register(m_dcpOnButtons);
|
||||||
|
register(m_dcpOnLinks);
|
||||||
|
register(m_enableTreeSelect);
|
||||||
|
register(m_showClassName);
|
||||||
|
|
||||||
|
loadInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the configured <code>PresentationManger</code>.
|
||||||
|
*/
|
||||||
|
public final PresentationManager getPresentationManager() {
|
||||||
|
return (PresentationManager) get(m_presenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class getBasePageClass() {
|
||||||
|
return (Class) get(m_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I don't *want* to make this public. XXX
|
||||||
|
*/
|
||||||
|
public final String getTidyConfigFile() {
|
||||||
|
return (String) get(m_tidy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wantFancyXSLErrors() {
|
||||||
|
return ((Boolean)get(m_fancyErrors)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean doubleClickProtectionOnButtons() {
|
||||||
|
return ((Boolean) get(m_dcpOnButtons)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean doubleClickProtectionOnLinks() {
|
||||||
|
return ((Boolean) get(m_dcpOnLinks)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean treeSelectAttributeEnabled() {
|
||||||
|
return ((Boolean) get(m_enableTreeSelect)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the DHTML editor to use
|
||||||
|
*/
|
||||||
|
public final String getDHTMLEditor() {
|
||||||
|
return (String) get(m_dhtmlEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the location of DHTML editor's source file
|
||||||
|
*/
|
||||||
|
public final String getDHTMLEditorSrcFile() {
|
||||||
|
return (String) get(m_dhtmlEditorSrcFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean showClassName() {
|
||||||
|
return ((Boolean) get(m_showClassName)).booleanValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.form.Hidden;
|
||||||
|
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.bebop.util.PanelConstraints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container that prints its components in one row, either horizontally or
|
||||||
|
* vertically.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: BoxPanel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class BoxPanel extends SimpleContainer
|
||||||
|
implements BebopConstants, PanelConstraints {
|
||||||
|
|
||||||
|
/** Specifies that components should be laid out left to right. */
|
||||||
|
public final static int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
/** Specifies that components should be laid out top to bottom. */
|
||||||
|
public final static int VERTICAL = 2;
|
||||||
|
|
||||||
|
/** XML attribute for width */
|
||||||
|
private static final String WIDTH_ATTR = "width";
|
||||||
|
|
||||||
|
/** XML attribute wether to draw a border. */
|
||||||
|
private static final String BORDER_ATTR = "border";
|
||||||
|
|
||||||
|
/** Property to whether to draw a HORIZONTAL or VERTICAL box panel. */
|
||||||
|
private int m_axis;
|
||||||
|
|
||||||
|
/** Property to store whether to to center alignment. */
|
||||||
|
private boolean m_centering;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, creates a box panel that lays out its components from
|
||||||
|
* top to bottom. The components are not centered.
|
||||||
|
*/
|
||||||
|
public BoxPanel() {
|
||||||
|
this(VERTICAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, creates a box panel that lays out its components in the given
|
||||||
|
* direction. The components are not centered.
|
||||||
|
*
|
||||||
|
* @param axis the axis to use to lay out the components
|
||||||
|
*/
|
||||||
|
public BoxPanel(int axis) {
|
||||||
|
this(axis, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a box panel that lays out its components in the given
|
||||||
|
* direction and centers them if that is specified.
|
||||||
|
*
|
||||||
|
* @param axis the axis to use to lay out the components
|
||||||
|
* @param centering <code>true</code> if the layout should be centered
|
||||||
|
*/
|
||||||
|
public BoxPanel(int axis, boolean centering) {
|
||||||
|
m_axis = axis;
|
||||||
|
m_centering = centering;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the width attribute of the box panel. The given width should be in
|
||||||
|
* a form that is legal as the <code>width</code> attribute of an HTML
|
||||||
|
* <code>table</code> element.
|
||||||
|
*
|
||||||
|
* @param w the width of the box panel
|
||||||
|
*/
|
||||||
|
public void setWidth(String w) {
|
||||||
|
setAttribute(WIDTH_ATTR, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Sets whether a border should be drawn.
|
||||||
|
// *
|
||||||
|
// * @param isBorder <code>true</code> if a border should be drawn
|
||||||
|
// * @deprecated Use {@link #setBorder(int border)} instead.
|
||||||
|
// */
|
||||||
|
// public void setBorder(boolean isBorder) {
|
||||||
|
// if (isBorder) {
|
||||||
|
// setAttribute(BORDER_ATTR, "1");
|
||||||
|
// } else {
|
||||||
|
// setAttribute(BORDER_ATTR, "0");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Sets the width of the border to draw around the components. This value
|
||||||
|
* will be used for the <code>border</code> attribute in an HTML
|
||||||
|
* <code>table</code> element.
|
||||||
|
*
|
||||||
|
* @param border the width of the border
|
||||||
|
*/
|
||||||
|
public void setBorder(int border) {
|
||||||
|
setAttribute(BORDER_ATTR, String.valueOf(border));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds nodes for the panel and its child components to be rendered,
|
||||||
|
* usually in a table. Any hidden widgets directly contained in the box
|
||||||
|
* panel are added directly to <code>parent</code> and are not in any
|
||||||
|
* of the cells that the box panel generates.
|
||||||
|
*
|
||||||
|
* <p>Generates DOM fragment:
|
||||||
|
* <p><code><bebop:boxPanel [width=...] border=... center... axis...>
|
||||||
|
* <bebop:cell> cell contents </bebop:cell>
|
||||||
|
* </bebop:boxPanel></code>
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
if (isVisible(state)) {
|
||||||
|
Element panel = parent.newChildElement(BEBOP_BOXPANEL, BEBOP_XML_NS);
|
||||||
|
// or: rowPanel/columPanel?
|
||||||
|
panel.addAttribute("center", String.valueOf(m_centering));
|
||||||
|
panel.addAttribute("axis", String.valueOf(m_axis));
|
||||||
|
exportAttributes(panel);
|
||||||
|
|
||||||
|
for (Iterator i = children(); i.hasNext();) {
|
||||||
|
Component c = (Component) i.next();
|
||||||
|
|
||||||
|
if (c.isVisible(state)) {
|
||||||
|
if (c instanceof Hidden) {
|
||||||
|
c.generateXML(state, parent);
|
||||||
|
} else {
|
||||||
|
Element cell = panel.newChildElement(BEBOP_CELL, BEBOP_XML_NS);
|
||||||
|
c.generateXML(state, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,574 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.form.Hidden;
|
||||||
|
import com.arsdigita.bebop.util.Attributes;
|
||||||
|
import com.arsdigita.bebop.util.PanelConstraints;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container that prints its components in a table. Each child is printed
|
||||||
|
* in its own table cell. The number of columns can be specified in the
|
||||||
|
* constructor. The components are put into the table in the order in which
|
||||||
|
* they were added to the <code>ColumnPanel</code> by filling the table one row
|
||||||
|
* at a time (filling each row from left to right), from the top of the table
|
||||||
|
* to the bottom.
|
||||||
|
*
|
||||||
|
* <p> The position of the component within the cell can be influenced with the
|
||||||
|
* following constraints.
|
||||||
|
* <TABLE border=0>
|
||||||
|
* <tr>
|
||||||
|
* <TD nowrap valign="top">Horizontal alignment</TD>
|
||||||
|
* <Td valign="top">Use <code>LEFT</code>, <code>CENTER</code>, or
|
||||||
|
* <code>RIGHT</code>.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Vertical alignment</td>
|
||||||
|
* <td valign="top">Use <code>TOP</code>, <code>MIDDLE</code>, or
|
||||||
|
* <code>BOTTOM</code>.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Full width</td>
|
||||||
|
* <td valign="top">Use <code>FULL_WIDTH</code> to instruct the panel to
|
||||||
|
* put the component in a row by itself, spanning the full width of the
|
||||||
|
* table.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Inserting children</td>
|
||||||
|
* <td valign="top">Use <code>INSERT</code> to instruct the panel to
|
||||||
|
* insert the corresponding component, assuming that it will also be
|
||||||
|
* laid out by a <code>ColumnPanel</code> with the same number of
|
||||||
|
* columns.</td></tr>
|
||||||
|
* </TABLE>
|
||||||
|
*
|
||||||
|
* <p>Constraints can be combined by OR-ing them together. For example, to print
|
||||||
|
* a component in a row of its own, left-aligned, at the bottom of its cell,
|
||||||
|
* use the constraint <code>FULL_WIDTH | LEFT | BOTTOM</code>.
|
||||||
|
*
|
||||||
|
* <p> Using the <code>INSERT</code> constraint fuses the current ColumnPanel
|
||||||
|
* with the panel of the child to which the constraint is applied. For example,
|
||||||
|
* consider a {@link Form} that is to have a 2-column format with labels in the
|
||||||
|
* left column and widgets in the right column. If a {@link FormSection} is
|
||||||
|
* added to the form, it should be included seamlessly into the parent form.
|
||||||
|
* To do this, set the <code>INSERT</code> constraint when the {@link
|
||||||
|
* FormSection} is added to the <code>ColumnPanel</code> of the {@link Form}. At
|
||||||
|
* the same time, tell the <code>ColumnPanel</code> used to lay out the {@link
|
||||||
|
* FormSection} that it is to be inserted into another panel.
|
||||||
|
*
|
||||||
|
* <P>The following pseudo-code illustrates the example. (It assumes that
|
||||||
|
* Form and FormSection are decorators of the ColumnPanel.)
|
||||||
|
*
|
||||||
|
* <pre style="background: #cccccc">
|
||||||
|
*
|
||||||
|
* Form form = new Form(new ColumnPanel(2));
|
||||||
|
* FormSection sec = new FormSection(new ColumnPanel(2, true));
|
||||||
|
*
|
||||||
|
* sec.add(new Label("Basic Item Metadata"), ColumnPanel.FULL_WIDTH);
|
||||||
|
* sec.add(new Label("Title:"), ColumnPanel.RIGHT);
|
||||||
|
* sec.add(new Text("title"));
|
||||||
|
*
|
||||||
|
* form.add(sec, ColumnPanel.INSERT);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: ColumnPanel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class ColumnPanel extends SimpleContainer
|
||||||
|
implements PanelConstraints {
|
||||||
|
|
||||||
|
/** An empty constraint corresponding to the default */
|
||||||
|
private static final Constraint DEFAULT_CONSTRAINT = new Constraint();
|
||||||
|
|
||||||
|
/** The number of columns in the panel */
|
||||||
|
private int m_nCols;
|
||||||
|
|
||||||
|
/** Explicitly registered constraints for child components. Maps
|
||||||
|
* <code>Components</code>s to <code>Constraints</code> */
|
||||||
|
private Map m_constraints;
|
||||||
|
|
||||||
|
/** Is this panel inserted in another one ? If so, do not produce
|
||||||
|
* <table> tags */
|
||||||
|
private boolean m_inserted;
|
||||||
|
|
||||||
|
/** Border attributes */
|
||||||
|
private Attributes m_border;
|
||||||
|
private Attributes m_padFrame;
|
||||||
|
private Attributes m_pad;
|
||||||
|
private String[] m_columnWidth;
|
||||||
|
|
||||||
|
|
||||||
|
// Instance methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table panel with the specified number of columns.
|
||||||
|
*
|
||||||
|
* @param nCols number of columns in the panel
|
||||||
|
*/
|
||||||
|
public ColumnPanel(int nCols) {
|
||||||
|
this(nCols, false);
|
||||||
|
makeBorder();
|
||||||
|
makePadFrame();
|
||||||
|
makePad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table panel with the specified number of columns that will
|
||||||
|
* be printed as a direct child of a <code>ColumnPanel</code>
|
||||||
|
* with the same number of columns.
|
||||||
|
* @see #setInserted
|
||||||
|
*
|
||||||
|
* @param nCols number of columns in the panel
|
||||||
|
* @param inserted <code>true</code> if this panel
|
||||||
|
* is to be printed as a direct child of a
|
||||||
|
* <code>ColumnPanel</code> with the same number of
|
||||||
|
* columns
|
||||||
|
*/
|
||||||
|
public ColumnPanel(int nCols, boolean inserted) {
|
||||||
|
m_nCols = nCols;
|
||||||
|
setInserted(inserted);
|
||||||
|
m_constraints = new HashMap();
|
||||||
|
m_columnWidth=new String[nCols];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component, specifying its constraints.
|
||||||
|
*
|
||||||
|
* @param c the component to add
|
||||||
|
* @param constraints the constraints for this component
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void add(Component c, int constraints) {
|
||||||
|
super.add(c);
|
||||||
|
setConstraint(c, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this panel will be printed inside a
|
||||||
|
* <code>ColumnPanel</code> with the same number of columns. If
|
||||||
|
* <code>inserted</code> is true, no <table> tags will be produced
|
||||||
|
* to enclose the child components.
|
||||||
|
* @param inserted <code>true</code> if this panel is to be printed
|
||||||
|
* inside a <code>ColumnPanel</code> with the same number of columns
|
||||||
|
*/
|
||||||
|
public void setInserted(boolean inserted) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_inserted = inserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether this panel is to be inserted into another panel.
|
||||||
|
* @return <code>true</code> if this panel is to be inserted
|
||||||
|
* into another panel; <code>false</code> otherwise.
|
||||||
|
*
|
||||||
|
* @see #setInserted
|
||||||
|
*/
|
||||||
|
public final boolean isInserted() {
|
||||||
|
return m_inserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of columns for this ColumnPanel
|
||||||
|
* @return the number of columns
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final int getNumCols() {
|
||||||
|
return m_nCols;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds child components as a subtree under table-style nodes. If any of the
|
||||||
|
* direct children are hidden form widgets, they are added directly to
|
||||||
|
* <code>parent</code> rather than included in any of the
|
||||||
|
* <code>cell</code> elements of the panel.
|
||||||
|
*
|
||||||
|
* <p>Generates a DOM fragment:
|
||||||
|
* <p><code><pre>
|
||||||
|
* <bebop:pad>
|
||||||
|
* [<bebop:padFrame>]
|
||||||
|
* [<bebop:border>]
|
||||||
|
* <bebop:panelRow>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* ...
|
||||||
|
* </bebop:panelRow>
|
||||||
|
* <bebop:panelRow>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* ...
|
||||||
|
* </bebop:panelRow>
|
||||||
|
* [</bebop:border>]
|
||||||
|
* [</bebop:padFrame>]
|
||||||
|
* </bebop:boxPanel></pre></code>
|
||||||
|
* @param state the current page state
|
||||||
|
* @param parent the parent element for these child components
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
if ( isVisible(state) ) {
|
||||||
|
|
||||||
|
Element panel = parent.newChildElement("bebop:columnPanel", BEBOP_XML_NS);
|
||||||
|
exportAttributes(panel);
|
||||||
|
// parent.addContent(panel);
|
||||||
|
|
||||||
|
generateChildren(state, parent, generateTopNodes(panel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Border attributes
|
||||||
|
|
||||||
|
private void makeBorder() {
|
||||||
|
if ( m_border == null ) {
|
||||||
|
m_border = new Attributes();
|
||||||
|
m_border.setAttribute("cellspacing", "0");
|
||||||
|
m_border.setAttribute("cellpadding", "4");
|
||||||
|
m_border.setAttribute("border", "0");
|
||||||
|
m_border.setAttribute("width", "100%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
*/
|
||||||
|
public void setBorderColor(String c) {
|
||||||
|
makeBorder();
|
||||||
|
m_border.setAttribute("bgcolor", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
*/
|
||||||
|
public void setBorderWidth(String w) {
|
||||||
|
makeBorder();
|
||||||
|
m_border.setAttribute("cellpadding", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColumnWidth(int col, String width) {
|
||||||
|
m_columnWidth[col-1]=width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
*/
|
||||||
|
public void setBorder(boolean b) {
|
||||||
|
if (b) {
|
||||||
|
makeBorder();
|
||||||
|
} else {
|
||||||
|
m_border = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad and Padframe attributes
|
||||||
|
private void makePadFrame() {
|
||||||
|
if (m_padFrame == null) {
|
||||||
|
m_padFrame = new Attributes();
|
||||||
|
m_padFrame.setAttribute("cellspacing", "0");
|
||||||
|
m_padFrame.setAttribute("cellpadding", "6");
|
||||||
|
m_padFrame.setAttribute("border", "0");
|
||||||
|
m_padFrame.setAttribute("width", "100%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void makePad() {
|
||||||
|
if ( m_pad == null ) {
|
||||||
|
m_pad = new Attributes();
|
||||||
|
m_pad.setAttribute("cellspacing", "0");
|
||||||
|
m_pad.setAttribute("cellpadding", "2");
|
||||||
|
m_pad.setAttribute("border", "0");
|
||||||
|
m_pad.setAttribute("width", "100%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
*/
|
||||||
|
public void setPadColor(String c) {
|
||||||
|
makePadFrame();
|
||||||
|
makePad();
|
||||||
|
m_padFrame.setAttribute("bgcolor", c);
|
||||||
|
m_pad.setAttribute("bgcolor", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
*/
|
||||||
|
public void setWidth(String w) {
|
||||||
|
makePadFrame();
|
||||||
|
m_padFrame.setAttribute("width", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param w
|
||||||
|
*/
|
||||||
|
public void setPadFrameWidth(String w) {
|
||||||
|
makePadFrame();
|
||||||
|
m_padFrame.setAttribute("cellpadding", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param border
|
||||||
|
*/
|
||||||
|
public void setPadBorder(boolean border) {
|
||||||
|
makePad();
|
||||||
|
if(border) {
|
||||||
|
m_pad.setAttribute("border", "1");
|
||||||
|
} else {
|
||||||
|
m_pad.setAttribute("border", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param padding
|
||||||
|
*/
|
||||||
|
public void setPadCellPadding(String padding) {
|
||||||
|
makePad();
|
||||||
|
m_pad.setAttribute("cellpadding", padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add top tags (will translate to opening/closing),
|
||||||
|
* including display styles
|
||||||
|
*/
|
||||||
|
private Element generateTopNodes(Element parent) {
|
||||||
|
// FIXME: set background color, border effects, cell spacing etc.
|
||||||
|
if (isInserted()) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
String l_class = getClassAttr();
|
||||||
|
if (m_border != null) {
|
||||||
|
Element border = parent.newChildElement("bebop:border",BEBOP_XML_NS);
|
||||||
|
if (l_class != null) {
|
||||||
|
m_border.setAttribute("class", l_class);
|
||||||
|
}
|
||||||
|
m_border.exportAttributes(border);
|
||||||
|
// parent.addContent(border);
|
||||||
|
parent=border; // nest the rest inside border
|
||||||
|
}
|
||||||
|
if ( m_padFrame != null ) {
|
||||||
|
Element padFrame = parent.newChildElement("bebop:padFrame", BEBOP_XML_NS);
|
||||||
|
if (l_class != null) {
|
||||||
|
m_padFrame.setAttribute("class", l_class);
|
||||||
|
}
|
||||||
|
m_padFrame.exportAttributes(padFrame);
|
||||||
|
// parent.addContent(padFrame);
|
||||||
|
parent=padFrame; // nest the rest in padFrame
|
||||||
|
}
|
||||||
|
Element pad = parent.newChildElement("bebop:pad", BEBOP_XML_NS);
|
||||||
|
if (l_class != null) {
|
||||||
|
m_pad.setAttribute("class", l_class);
|
||||||
|
}
|
||||||
|
m_pad.exportAttributes(pad);
|
||||||
|
// parent.addContent(pad);
|
||||||
|
return pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lay out the child components using constraints registered for them,
|
||||||
|
* generating a DOM tree and extending another.
|
||||||
|
*
|
||||||
|
* @param state represents the state of the current request
|
||||||
|
* @param hiddenParent the element to which hiddens are added
|
||||||
|
* @param parent the element to which ordinary rows and cells are added
|
||||||
|
*/
|
||||||
|
private void generateChildren(PageState state, Element hiddenParent,
|
||||||
|
Element parent) {
|
||||||
|
// Count the number of components printed in the current row
|
||||||
|
int rowLen = m_nCols + 1; // Force generation of first row
|
||||||
|
Element row = null;
|
||||||
|
Element cell = null;
|
||||||
|
|
||||||
|
for (Iterator i = children(); i.hasNext(); ) {
|
||||||
|
Component c = (Component) i.next();
|
||||||
|
|
||||||
|
if ( c.isVisible(state) ) {
|
||||||
|
|
||||||
|
if ( c instanceof Hidden ) {
|
||||||
|
c.generateXML(state, hiddenParent);
|
||||||
|
} else {
|
||||||
|
if ( isInsert(c) ) {
|
||||||
|
c.generateXML(state, parent);
|
||||||
|
rowLen = m_nCols + 1; // Force generation of new row
|
||||||
|
} else {
|
||||||
|
if ( rowLen >= m_nCols || isFullWidth(c)) {
|
||||||
|
rowLen = 0;
|
||||||
|
row = parent.newChildElement("bebop:panelRow", BEBOP_XML_NS);
|
||||||
|
// parent.addContent(row);
|
||||||
|
}
|
||||||
|
cell = row.newChildElement("bebop:cell", BEBOP_XML_NS);
|
||||||
|
// row.addContent(cell);
|
||||||
|
if ( m_columnWidth[rowLen] != null ) {
|
||||||
|
cell.addAttribute("width", m_columnWidth[rowLen]);
|
||||||
|
}
|
||||||
|
getConstraint(c).exportAttributes(cell, m_nCols);
|
||||||
|
c.generateXML(state, cell);
|
||||||
|
rowLen++;
|
||||||
|
if ( isFullWidth(c) ) {
|
||||||
|
// Force a new row if c was full width
|
||||||
|
rowLen = m_nCols + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the constraint for one child component.
|
||||||
|
* @param c the child component
|
||||||
|
* @param constraints the constraints to add
|
||||||
|
*/
|
||||||
|
public void setConstraint(Component c, int constraints) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_constraints.put(c, new Constraint(constraints));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the constraint object for a component. If no constraints have been
|
||||||
|
* set explicitly, return a default constraint object.
|
||||||
|
*
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
private Constraint getConstraint(Component c) {
|
||||||
|
Constraint result = (Constraint) m_constraints.get(c);
|
||||||
|
if ( result == null ) {
|
||||||
|
return DEFAULT_CONSTRAINT;
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInsert(Component c) {
|
||||||
|
return getConstraint(c).isInsert();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFullWidth(Component c) {
|
||||||
|
return getConstraint(c).isFullWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Inner class(es)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent the constraints for one child component
|
||||||
|
*/
|
||||||
|
private static class Constraint {
|
||||||
|
private boolean m_fullWidth;
|
||||||
|
private boolean m_insert;
|
||||||
|
private String m_alignment; // for print
|
||||||
|
private String m_halign; // for generateXML
|
||||||
|
private String m_valign; // for generateXML
|
||||||
|
|
||||||
|
public Constraint() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Constraint(int constraints) {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
|
||||||
|
if ( (constraints & (LEFT|CENTER|RIGHT)) != 0 ) {
|
||||||
|
s.append(" align=\"");
|
||||||
|
if ( (constraints & LEFT) != 0) {
|
||||||
|
s.append(m_halign = "left");
|
||||||
|
} else if ( (constraints & CENTER) != 0) {
|
||||||
|
s.append(m_halign = "center");
|
||||||
|
} else if ( (constraints & RIGHT) != 0) {
|
||||||
|
s.append(m_halign = "right");
|
||||||
|
}
|
||||||
|
s.append("\" ");
|
||||||
|
} else {
|
||||||
|
m_halign = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (constraints & (TOP|MIDDLE|BOTTOM)) != 0 ) {
|
||||||
|
s.append(" valign=\"");
|
||||||
|
if ( (constraints & TOP) != 0) {
|
||||||
|
s.append(m_valign = "top");
|
||||||
|
} else if ( (constraints & MIDDLE) != 0) {
|
||||||
|
s.append(m_valign = "middle");
|
||||||
|
} else if ( (constraints & BOTTOM) != 0) {
|
||||||
|
s.append(m_valign = "bottom");
|
||||||
|
}
|
||||||
|
s.append("\" ");
|
||||||
|
} else {
|
||||||
|
m_valign = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_alignment = s.toString();
|
||||||
|
|
||||||
|
m_fullWidth = (constraints & FULL_WIDTH) != 0;
|
||||||
|
m_insert = (constraints & INSERT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isFullWidth() {
|
||||||
|
return m_fullWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isInsert() {
|
||||||
|
return m_insert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getAlignment() {
|
||||||
|
return m_alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getHAlign() {
|
||||||
|
return m_halign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getVAlign() {
|
||||||
|
return m_valign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exportAttributes(Element cell, int nCols) {
|
||||||
|
String halign = getHAlign();
|
||||||
|
String valign = getVAlign();
|
||||||
|
if (halign != null) {
|
||||||
|
cell.addAttribute("align" , halign);
|
||||||
|
}
|
||||||
|
if (valign != null) {
|
||||||
|
cell.addAttribute("valign", valign);
|
||||||
|
}
|
||||||
|
if ( isFullWidth() ) {
|
||||||
|
cell.addAttribute("colspan", Integer.toString(nCols));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.ActionEvent;
|
||||||
|
import com.arsdigita.bebop.event.ActionListener;
|
||||||
|
// Stacktraces is a support tool to use in a specifically difficult development
|
||||||
|
// situation. It is abundant in production and for normal development work and
|
||||||
|
// it provved to have funny side effects in a production environment. So it is
|
||||||
|
// commented out here but kept for further references.
|
||||||
|
// import com.arsdigita.developersupport.StackTraces;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completable.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:rhs@mit.edu">rhs@mit.edu</a>
|
||||||
|
* @version $Revision: #10 $ $Date: 2004/08/16 $
|
||||||
|
* @version $Id: Completable.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
**/
|
||||||
|
|
||||||
|
public abstract class Completable implements Component {
|
||||||
|
|
||||||
|
private final static Logger s_log = Logger.getLogger(Completable.class);
|
||||||
|
|
||||||
|
private ArrayList m_completionListeners = new ArrayList();
|
||||||
|
|
||||||
|
public Completable() {
|
||||||
|
// See note above!
|
||||||
|
// if ( s_log.isDebugEnabled() ) {
|
||||||
|
// StackTraces.captureStackTrace(this);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCompletionListener(ActionListener listener) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_completionListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireCompletionEvent(PageState ps) {
|
||||||
|
ActionEvent evt = new ActionEvent(this, ps);
|
||||||
|
for (Iterator it = m_completionListeners.iterator(); it.hasNext(); ) {
|
||||||
|
ActionListener listener = (ActionListener) it.next();
|
||||||
|
listener.actionPerformed(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,387 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Lockable;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The common interface implemented by all Bebop components.
|
||||||
|
*
|
||||||
|
* During its lifetime, a component receives the following calls
|
||||||
|
* from the containing page.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li> During initialization/creation of the containing page:
|
||||||
|
* <ul>
|
||||||
|
* <li> {@link #register(Page)} is called to register
|
||||||
|
* state parameters that need to be preserved between requests to
|
||||||
|
* the same page.
|
||||||
|
* </li>
|
||||||
|
* <li> {@link #register(Form, FormModel)} is called if
|
||||||
|
* the component is contained
|
||||||
|
* in a {@link Form} or {@link FormSection}. Typically, only form
|
||||||
|
* widgets like text controls have parameters that need to be
|
||||||
|
* registered with the <code>FormSection</code>.
|
||||||
|
* </li>
|
||||||
|
* <li> {@link Lockable#lock lock} is called to lock the component and
|
||||||
|
* inform it that no further structural modifications will be
|
||||||
|
* made.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li> For each request made to the containing page:
|
||||||
|
* <ul>
|
||||||
|
* <li> If the request originated from
|
||||||
|
* this component, {@link #respond(PageState) respond} is called.
|
||||||
|
* </li>
|
||||||
|
* <li> To produce output, {@link #generateXML(PageState, Element) generateXML} is
|
||||||
|
* called to produce XML output that will be transformed by the
|
||||||
|
* templating system.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h4><a name="visibility">Visibility</a></h4>
|
||||||
|
*
|
||||||
|
* <p>A component can be either <i>visible</i> or
|
||||||
|
* <i>invisible</i>. Invisible components do not produce any output and
|
||||||
|
* containers should be careful to completely hide their presence. The
|
||||||
|
* visibility of a component can be influenced in a number of ways:
|
||||||
|
* <UL>
|
||||||
|
* <LI>When a
|
||||||
|
* component is first added to the hierarchy of a {@link Page}, it is
|
||||||
|
* visible.</LI>
|
||||||
|
* <LI> A component's default (request-independent) visibility can be changed
|
||||||
|
* with a call to {@link Page#setVisibleDefault
|
||||||
|
* Page.setVisibleDefault} during setup of the page.</LI>
|
||||||
|
* <LI>A component
|
||||||
|
* can be made visible or invisible during the serving of a request with a
|
||||||
|
* call to {@link #setVisible setVisible}.</LI>
|
||||||
|
* </UL>
|
||||||
|
* <p> The {@link Page} makes sure that the visibility of components is
|
||||||
|
* preserved across repeated requests to the same page.
|
||||||
|
*
|
||||||
|
* <h4><a name="standard">Standard Attributes</a></h4>
|
||||||
|
* <p> Each component supports a few standard attributes that are copied
|
||||||
|
* through to the output when producing either HTML or XML
|
||||||
|
* output. These attributes are not used internally in any way, and setting
|
||||||
|
* them is entirely optional.
|
||||||
|
*
|
||||||
|
* <p> The standard attributes appear in the output as attributes in
|
||||||
|
* the element generated from this component. They correspond directly to
|
||||||
|
* properties with setters and getters. The standard attributes are as folows.
|
||||||
|
* <center><table cellspacing=5 cellpadding=2 border=0>
|
||||||
|
* <tr>
|
||||||
|
* <th>Attribute</th> <th>Java property</th> <th>Purpose</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td valign='top'><tt>id</tt></td>
|
||||||
|
* <td valign='top'>
|
||||||
|
* <tt>{@link #getIdAttr getIdAttr}/{@link #setIdAttr setIdAttr}</tt>
|
||||||
|
* </td>
|
||||||
|
* <td>Use to uniquely identify a component within the page. Uniqueness is
|
||||||
|
* not enforced. The <tt>id</tt> attribute allows stylesheet designers to
|
||||||
|
* access individual components.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td valign='top'><tt>class</tt></td>
|
||||||
|
* <td valign='top'><tt>{@link #getClassAttr()}/{@link
|
||||||
|
* #setClassAttr(String)}</tt></td>
|
||||||
|
*
|
||||||
|
* <td>Use as the generic name for a
|
||||||
|
* component. For example, if you have a UserInfoDisplay
|
||||||
|
* component, the class attribute can be used to name the
|
||||||
|
* component "UserInfoDisplay" so that a generic template rule
|
||||||
|
* can style all UserInfoDisplay components.</td>
|
||||||
|
*
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td valign='top'><tt>style</tt></td>
|
||||||
|
* <td valign='top'><tt>{@link #getStyleAttr getStyleAttr}/{@link
|
||||||
|
* #setStyleAttr setStyleAttr}</tt></td>
|
||||||
|
* <td>Use to add CSS style to an individual element.</td>
|
||||||
|
* </tr>
|
||||||
|
*
|
||||||
|
* </table></center>
|
||||||
|
*
|
||||||
|
* <h4><a name="caveat">Caveat: Race Conditions</a></h4>
|
||||||
|
* <p>
|
||||||
|
* When extending any <code>Component</code>, honor the
|
||||||
|
* <em>Lockable</em> contract indicated by <code>extends {@link
|
||||||
|
* com.arsdigita.util.Lockable}</code>. Beware that member variables
|
||||||
|
* are not inherently threadsafe, because you may be circumventing
|
||||||
|
* the contract. For variables that might be different for each
|
||||||
|
* request, use {@link RequestLocal}. If you must add member
|
||||||
|
* variables in the derived class, as a minimum be sure to safeguard
|
||||||
|
* any write access to instance variables with {@link
|
||||||
|
* com.arsdigita.util.Assert#assertNotLocked}.
|
||||||
|
* </p>
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface Component extends Lockable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML namespace used by all the Bebop components.
|
||||||
|
*/
|
||||||
|
String BEBOP_XML_NS =
|
||||||
|
"http://www.arsdigita.com/bebop/1.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name for the class attribute.
|
||||||
|
* @see #setClassAttr(String)
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
String CLASS = "class";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name for the style attribute.
|
||||||
|
* @see #setStyleAttr(String)
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
String STYLE = "style";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name for the ID attribute.
|
||||||
|
* @see #setIdAttr
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
String ID = "id";
|
||||||
|
|
||||||
|
// HTML 4 event names
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The onClick event.
|
||||||
|
*/
|
||||||
|
String ON_CLICK = "onclick";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Adds a DOM subtree representing this component under the given
|
||||||
|
* parent node. Uses the request values stored in <code>state</code>.</p>
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
* @param parent the node under which the DOM subtree should be added
|
||||||
|
*
|
||||||
|
* @pre state != null
|
||||||
|
* @pre parent != null
|
||||||
|
*/
|
||||||
|
void generateXML(PageState state, Element parent);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Responds to the request. This method is only called if the request
|
||||||
|
* was made from a link or form that the component put on the page in the
|
||||||
|
* {@link PageState#stateAsURL} previous request.</p>
|
||||||
|
*
|
||||||
|
* <p>No output should be generated on the HTTP response. The component
|
||||||
|
* can store intermediate results in the <code>state</code> by calling
|
||||||
|
* {@link PageState#setAttribute setAttribute}.</p>
|
||||||
|
*
|
||||||
|
* <p> This method is called before any output is printed to the HTTP
|
||||||
|
* response so that the component can forward to a different page and
|
||||||
|
* thereby commit the response.</p>
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
*
|
||||||
|
* @pre state != null
|
||||||
|
*/
|
||||||
|
void respond(PageState state)
|
||||||
|
throws javax.servlet.ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over the children of this component. If the
|
||||||
|
* component has no children, returns an empty (not
|
||||||
|
* <code>null</code>) iterator.
|
||||||
|
*
|
||||||
|
* @return an iterator over the children of this component.
|
||||||
|
*
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
Iterator children();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers state parameters for the page with its model.
|
||||||
|
*
|
||||||
|
* A simple component with a state parameter <code>param</code> would do
|
||||||
|
* the following in the body of this method:
|
||||||
|
* <pre>
|
||||||
|
* p.addComponent(this);
|
||||||
|
* p.addComponentStateParam(this, param);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* You should override this method to set the default visibility
|
||||||
|
* of your component:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public void register(Page p) {
|
||||||
|
* super.register(p);
|
||||||
|
* p.setVisibleDefault(childNotInitiallyShown,false);
|
||||||
|
* p.setVisibleDefault(anotherChild, false);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Always call <code>super.register</code> when you override
|
||||||
|
* <code>register</code>. Otherwise your component may
|
||||||
|
* malfunction and produce errors like "Widget ... isn't
|
||||||
|
* associated with any Form"
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* @pre p != null
|
||||||
|
*/
|
||||||
|
void register(Page p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers form parameters with the form model for this
|
||||||
|
* form. This method is only important for {@link FormSection form
|
||||||
|
* sections} and {@link com.arsdigita.bebop.form.Widget widgets}
|
||||||
|
* (components that have a connection to an HTML form). Other
|
||||||
|
* components can implement it as a no-op.
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* @param m
|
||||||
|
* @pre f != null
|
||||||
|
* @pre m != null
|
||||||
|
*/
|
||||||
|
void register(Form f, FormModel m);
|
||||||
|
|
||||||
|
/* Properties that will get copied straight to the output,
|
||||||
|
both in HTML and in XML
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class attribute.
|
||||||
|
*
|
||||||
|
* @return the class attribute.
|
||||||
|
*
|
||||||
|
* @see #setClassAttr(String)
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
String getClassAttr();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the class attribute.
|
||||||
|
* @param theClass a valid <a
|
||||||
|
* href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name">XML name</a>
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
* @see #getClassAttr
|
||||||
|
*/
|
||||||
|
void setClassAttr(String theClass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the style attribute.
|
||||||
|
*
|
||||||
|
* @return the style attribute.
|
||||||
|
*
|
||||||
|
* @see #setStyleAttr
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
String getStyleAttr();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the style attribute. <code>style</code> should be a valid CSS
|
||||||
|
* style, because its value will be copied verbatim to the output and
|
||||||
|
* appear as a <tt>style</tt> attribute in the top level XML or HTML
|
||||||
|
* output element.
|
||||||
|
*
|
||||||
|
* @param style a valid CSS style description for use in the
|
||||||
|
* <tt>style</tt> attribute of an HTML tag
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
void setStyleAttr(String style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the <tt>id</tt> attribute.
|
||||||
|
*
|
||||||
|
* @return the id attribute.
|
||||||
|
*
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
* @see #setIdAttr(String id)
|
||||||
|
*/
|
||||||
|
String getIdAttr();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>id</tt> attribute. <code>id</code>
|
||||||
|
* should be an <a
|
||||||
|
* href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name">XML name</a>
|
||||||
|
* that is unique within the {@link Page Page} in which this component is
|
||||||
|
* contained. The value of <code>id</code> is copied literally to the
|
||||||
|
* output and not used for internal processing.
|
||||||
|
*
|
||||||
|
* @param id a valid XML identifier
|
||||||
|
* @see <a href="Component#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
void setIdAttr(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplies a key for making parameter names unique. To be used
|
||||||
|
* instead of the component's index (see <a
|
||||||
|
* href="PageModel#componentPrefix">Component Prefix</a>).
|
||||||
|
* To avoid collision with indexOf, it
|
||||||
|
* should (1) be a legal fragment of a cgi parameter, (2) differ from "g",
|
||||||
|
* and (3) not start with a digit.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Component setKey(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the programmer-supplied key. Normally, there is no
|
||||||
|
* such key and the method returns null.
|
||||||
|
*
|
||||||
|
* @return the programmer-supplied key.
|
||||||
|
*/
|
||||||
|
String getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the component is visible in the request
|
||||||
|
* represented by <code>state</code>.
|
||||||
|
* @see #setVisible setVisible
|
||||||
|
* @see <a href="Component.html#visibility">Description of Visibility
|
||||||
|
* above</a>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
* @return <code>true</code> if the component is visible; <code>false</code>
|
||||||
|
* otherwise.
|
||||||
|
* @pre state != null
|
||||||
|
*/
|
||||||
|
boolean isVisible(PageState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the visibility of the component. The component will keep the
|
||||||
|
* visibility that is set with this method in subsequent requests to this page.
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
* @param v <code>true</code> if the component should be visible
|
||||||
|
* @pre state != null
|
||||||
|
* @see <a href="Component.html#visibility">Description of Visibility
|
||||||
|
* above</a>
|
||||||
|
*/
|
||||||
|
void setVisible(PageState state, boolean v);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.FormInitListener;
|
||||||
|
import com.arsdigita.bebop.event.FormProcessListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
|
import com.arsdigita.bebop.form.Submit;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.web.ParameterMap;
|
||||||
|
import com.arsdigita.web.RedirectSignal;
|
||||||
|
import com.arsdigita.web.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Bebop Confirmation Page which should be mounted at ConfirmPage.CONFIRM_URL by the BebopMapDispatcher.
|
||||||
|
* This page takes three URL parameters:
|
||||||
|
* <ul>
|
||||||
|
* <li>A confirmation message with variable name ConfirmPage.CONFIRM_MSG_VAR
|
||||||
|
* <li>An OK URL with variable name ConfirmPage.OK_URL_VAR
|
||||||
|
* <li>A Cancel URL with variable name ConfirmPage.CANCEL_URL_VAR
|
||||||
|
* </ul>
|
||||||
|
* The page displays a form asking the confirmation message passed in. If the user hits <i>OK</i>,
|
||||||
|
* Then the page redirects to the OK URL. Otherwise, if the user hits <i>Cancel<i/>,
|
||||||
|
* The page redirects to the Cancel URL.
|
||||||
|
* @author Bryan Che
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ConfirmPage extends Page {
|
||||||
|
private StringParameter m_ConfirmMsgParam;
|
||||||
|
private StringParameter m_OkUrlParam;
|
||||||
|
private StringParameter m_CancelUrlParam;
|
||||||
|
|
||||||
|
private RequestLocal m_ConfirmMsgRL;
|
||||||
|
private RequestLocal m_OkUrlRL;
|
||||||
|
private RequestLocal m_CancelUrlRL;
|
||||||
|
|
||||||
|
//URL at which to mount this page
|
||||||
|
public static final String CONFIRM_URL = "BEBOP-confirmation-page";
|
||||||
|
|
||||||
|
//URL variable names
|
||||||
|
private static final String CONFIRM_MSG_VAR = "confirm-msg";
|
||||||
|
private static final String OK_URL_VAR = "ok-url";
|
||||||
|
private static final String CANCEL_URL_VAR = "cancel-url";
|
||||||
|
|
||||||
|
public ConfirmPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
m_ConfirmMsgParam = new StringParameter(CONFIRM_MSG_VAR);
|
||||||
|
m_OkUrlParam = new StringParameter(OK_URL_VAR);
|
||||||
|
m_CancelUrlParam = new StringParameter(CANCEL_URL_VAR);
|
||||||
|
|
||||||
|
//add global state params
|
||||||
|
addGlobalStateParam(m_ConfirmMsgParam);
|
||||||
|
addGlobalStateParam(m_OkUrlParam);
|
||||||
|
addGlobalStateParam(m_CancelUrlParam);
|
||||||
|
|
||||||
|
//initialize RequestLocals for the URL params
|
||||||
|
m_ConfirmMsgRL = new RequestLocal() {
|
||||||
|
protected Object initialValue(PageState ps) {
|
||||||
|
return ps.getValue(m_ConfirmMsgParam);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
m_OkUrlRL = new RequestLocal() {
|
||||||
|
protected Object initialValue(PageState ps) {
|
||||||
|
return ps.getValue(m_OkUrlParam);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
m_CancelUrlRL = new RequestLocal() {
|
||||||
|
protected Object initialValue(PageState ps) {
|
||||||
|
return ps.getValue(m_CancelUrlParam);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//set the title
|
||||||
|
buildTitle();
|
||||||
|
|
||||||
|
//add the form
|
||||||
|
ConfirmForm cf = new ConfirmForm(m_ConfirmMsgRL, m_OkUrlRL, m_CancelUrlRL);
|
||||||
|
add(cf);
|
||||||
|
|
||||||
|
lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a URL (minus "http://" string and server name) at which to access the ConfirmPage
|
||||||
|
* with the given Confirmation Message, OK URL, and Cancel URL.
|
||||||
|
* @param sConfirmMsg the Confirmation message to display on the page
|
||||||
|
* @param sOkUrl the URL to which to redirect if the user hits <i>OK</i>
|
||||||
|
* @param sCancelUrl the URL to which to redirect if the user hits <i>Cancel</i>
|
||||||
|
* @return URL at which to access the ConfirmPage
|
||||||
|
*/
|
||||||
|
public static String getConfirmUrl(String sConfirmMsg, String sOkUrl, String sCancelUrl) {
|
||||||
|
final ParameterMap params = new ParameterMap();
|
||||||
|
|
||||||
|
params.setParameter(CONFIRM_MSG_VAR, sConfirmMsg);
|
||||||
|
params.setParameter(OK_URL_VAR, sOkUrl);
|
||||||
|
params.setParameter(CANCEL_URL_VAR, sCancelUrl);
|
||||||
|
|
||||||
|
return URL.there("/" + CONFIRM_URL, params).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void buildTitle() {
|
||||||
|
class ConfirmPagePrintListener implements PrintListener {
|
||||||
|
public void prepare(PrintEvent e) {
|
||||||
|
Label label = (Label) e.getTarget();
|
||||||
|
PageState ps = e.getPageState();
|
||||||
|
|
||||||
|
label.setLabel((String)m_ConfirmMsgRL.get(ps));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTitle(new Label(new ConfirmPagePrintListener()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConfirmFormPrintListener implements PrintListener {
|
||||||
|
private RequestLocal m_RL;
|
||||||
|
|
||||||
|
ConfirmFormPrintListener(RequestLocal ConfirmMsgRL) {
|
||||||
|
m_RL = ConfirmMsgRL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare(PrintEvent e) {
|
||||||
|
Label label = (Label) e.getTarget();
|
||||||
|
PageState ps = e.getPageState();
|
||||||
|
|
||||||
|
label.setLabel((String)m_RL.get(ps) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConfirmForm extends Form implements FormInitListener, FormProcessListener {
|
||||||
|
private Label m_ConfirmMsgLabel;
|
||||||
|
private Submit m_OkButton;
|
||||||
|
private Submit m_CancelButton;
|
||||||
|
|
||||||
|
private RequestLocal m_OkRL;
|
||||||
|
private RequestLocal m_CancelRL;
|
||||||
|
|
||||||
|
private String m_sOkUrl = null;
|
||||||
|
private String m_sCancelUrl = null;
|
||||||
|
|
||||||
|
public ConfirmForm(RequestLocal ConfirmMsgRL, RequestLocal OkUrlRL, RequestLocal CancelUrlRL) {
|
||||||
|
super("ConfirmForm");
|
||||||
|
m_ConfirmMsgLabel = new Label(new ConfirmFormPrintListener(ConfirmMsgRL));
|
||||||
|
|
||||||
|
this.add(m_ConfirmMsgLabel);
|
||||||
|
|
||||||
|
m_OkButton = new Submit("OK");
|
||||||
|
m_OkButton.setButtonLabel("OK");
|
||||||
|
this.add(m_OkButton);
|
||||||
|
m_OkRL = OkUrlRL;
|
||||||
|
|
||||||
|
m_CancelButton = new Submit("Cancel");
|
||||||
|
m_CancelButton.setButtonLabel("Cancel");
|
||||||
|
this.add(m_CancelButton);
|
||||||
|
m_CancelRL = CancelUrlRL;
|
||||||
|
|
||||||
|
this.addInitListener(this);
|
||||||
|
this.addProcessListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FormSectionEvent e) {
|
||||||
|
PageState ps = e.getPageState();
|
||||||
|
|
||||||
|
//initialize the OK and Cancel URL's
|
||||||
|
m_sOkUrl = (String) m_OkRL.get(ps);
|
||||||
|
m_sCancelUrl = (String) m_CancelRL.get(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(FormSectionEvent e) {
|
||||||
|
PageState ps = e.getPageState();
|
||||||
|
|
||||||
|
if (m_OkButton.isSelected(ps)) {
|
||||||
|
throw new RedirectSignal(m_sOkUrl, true);
|
||||||
|
} else {
|
||||||
|
throw new RedirectSignal(m_sCancelUrl, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The common interface that is implemented by all Bebop containers.
|
||||||
|
*
|
||||||
|
* The Container interface extends the Component interface. A container is
|
||||||
|
* simply a component that contains other components.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id: Container.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
* */
|
||||||
|
public interface Container extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component to this container.
|
||||||
|
*
|
||||||
|
* @param pc component to add to this container
|
||||||
|
* @pre pc != null
|
||||||
|
*/
|
||||||
|
void add(Component pc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component with the specified layout constraints to this
|
||||||
|
* container. Layout constraints are defined in each layout container as
|
||||||
|
* static ints. To specify multiple constraints, uses bitwise OR.
|
||||||
|
*
|
||||||
|
* @param pc component to add to this container
|
||||||
|
*
|
||||||
|
* @param constraints layout constraints (a
|
||||||
|
* bitwise OR of static ints in the particular layout)
|
||||||
|
*
|
||||||
|
* @pre c != null
|
||||||
|
*/
|
||||||
|
void add(Component c, int constraints);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if this list contains the specified element.
|
||||||
|
* More formally, returns <code>true</code> if and only if this list
|
||||||
|
* contains at least
|
||||||
|
* one element e such that (o==null ? e==null : o.equals(e)).
|
||||||
|
* <P>
|
||||||
|
* This method returns <code>true</code> only if the object has been
|
||||||
|
* directly added to this container. If this container contains another
|
||||||
|
* container that contains this object, this method returns
|
||||||
|
* <code>false</code>.
|
||||||
|
*
|
||||||
|
* @param o element whose presence in this container is to be tested
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this container contains the specified
|
||||||
|
* object directly; <code>false</code> otherwise.
|
||||||
|
* @pre o != null
|
||||||
|
*/
|
||||||
|
boolean contains(Object o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the component
|
||||||
|
* at the specified position. Each call to the add method increments
|
||||||
|
* the index. Since the user has no control over the index of added
|
||||||
|
* components (other than counting each call to the add method),
|
||||||
|
* this method should be used in conjunction with indexOf.
|
||||||
|
*
|
||||||
|
* @param index the index of the item to be retrieved from this
|
||||||
|
* container
|
||||||
|
*
|
||||||
|
* @return the component at the specified position in this container.
|
||||||
|
*
|
||||||
|
* @pre index >= 0 && index < size()
|
||||||
|
* @post return != null */
|
||||||
|
Component get(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param pc component to search for
|
||||||
|
*
|
||||||
|
* @return the index in this list of the first occurrence of
|
||||||
|
* the specified element, or -1 if this list does not contain this
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @pre pc != null
|
||||||
|
* @post contains(pc) implies (return >= 0 && return < size())
|
||||||
|
* @post ! contains(pc) implies return == -1
|
||||||
|
*/
|
||||||
|
int indexOf(Component pc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the container contains no components.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this container contains no components;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
* @post return == ( size() == 0 )
|
||||||
|
*/
|
||||||
|
boolean isEmpty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements in this container. This does not
|
||||||
|
* recursively count components that are indirectly contained in this container.
|
||||||
|
*
|
||||||
|
* @return the number of components directly in this container.
|
||||||
|
* @post size() >= 0
|
||||||
|
*/
|
||||||
|
int size();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.ActionEvent;
|
||||||
|
import com.arsdigita.bebop.event.ActionListener;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link back to the page in which it is contained. The control link captures
|
||||||
|
* and preserves the current state of the page, and possibly any control events
|
||||||
|
* that have been set. It is most useful inside a {@link
|
||||||
|
* com.arsdigita.bebop.list.ListCellRenderer} or a {@link
|
||||||
|
* com.arsdigita.bebop.table.TableCellRenderer}, where the list or table has
|
||||||
|
* already set up the events 'tight' for the control link to do the right thing.
|
||||||
|
*
|
||||||
|
* <p> <b>Warning:</b> Even though a control link lets you add action
|
||||||
|
* listeners, they are not run unless you override {@link #setControlEvent
|
||||||
|
* setControlEvent}. If you need this behavior, you should use an {@link
|
||||||
|
* ActionLink}. A control link is hardly ever useful unless it is contained
|
||||||
|
* in an event-generating component like {@link List} or {@link Table}.
|
||||||
|
*
|
||||||
|
* <p> <b>Example:</b> A control link is mainly useful to send events to other
|
||||||
|
* components. For example, the following control link will cause a control
|
||||||
|
* event <tt>delete</tt> with associated value <tt>42</tt> to be sent to
|
||||||
|
* the component <tt>fooComponent</tt> when the user clicks on it:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* ControlLink l = new ControlLink("click here") {
|
||||||
|
* public void setControlEvent(PageState s) {
|
||||||
|
* s.setControlEvent(fooComponent, "delete", 42);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p> This requires that <tt>fooComponent</tt> is part of the page
|
||||||
|
* hierarchy. The control link <tt>l</tt> does not have to be part of the
|
||||||
|
* page hierarchy, and may be generated on the fly. (See
|
||||||
|
* {@link PageState} for details on control events.)
|
||||||
|
*
|
||||||
|
* <p>See {@link BaseLink} for a description
|
||||||
|
* of all Bebop Link classes.
|
||||||
|
*
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: ControlLink.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class ControlLink extends BaseLink {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML type attribute for a {@link ControlLink}.
|
||||||
|
*/
|
||||||
|
protected final String TYPE_CONTROL = "control";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all action listeners. The list is instantiated lazily, and
|
||||||
|
* will therefore be null in most applications.
|
||||||
|
*/
|
||||||
|
private ArrayList m_actionListeners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ControlLink. The link will encapsulates
|
||||||
|
* the child component (which should be a label or an image).
|
||||||
|
*
|
||||||
|
* @param child the component that will be turned into a link
|
||||||
|
*/
|
||||||
|
public ControlLink(Component child) {
|
||||||
|
super(child, "");
|
||||||
|
setTypeAttr(TYPE_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ControlLink with the given string label.
|
||||||
|
*
|
||||||
|
* @param label the string label for the link
|
||||||
|
*/
|
||||||
|
public ControlLink(String label) {
|
||||||
|
this(new Label(label));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an <code>ActionListener</code>, which will be run when {@link
|
||||||
|
* #respond respond} is called.
|
||||||
|
* @param 1 a listener to add
|
||||||
|
*
|
||||||
|
* @pre l != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
* @see #respond respond
|
||||||
|
*/
|
||||||
|
public void addActionListener(ActionListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if ( m_actionListeners == null ) {
|
||||||
|
m_actionListeners = new ArrayList();
|
||||||
|
}
|
||||||
|
m_actionListeners.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously added <code>ActionListener</code>.
|
||||||
|
* @param 1 the listener to remove
|
||||||
|
* @see #addActionListener addActionListener
|
||||||
|
*/
|
||||||
|
public void removeActionListener(ActionListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if ( m_actionListeners == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_actionListeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an <code>ActionEvent</code>, which causes all registered
|
||||||
|
* <code>ActionListener</code>s to be run. The source of the event
|
||||||
|
* is the <code>TabbedPane</code>.
|
||||||
|
* @param state the current page state
|
||||||
|
* @pre state != null
|
||||||
|
* @see #respond respond
|
||||||
|
*/
|
||||||
|
protected void fireActionEvent(PageState state) {
|
||||||
|
ActionEvent e = null;
|
||||||
|
if (m_actionListeners == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i=0; i < m_actionListeners.size(); i++ ) {
|
||||||
|
if ( e == null ) {
|
||||||
|
e = new ActionEvent(this, state);
|
||||||
|
}
|
||||||
|
((ActionListener) m_actionListeners.get(i)).actionPerformed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to the incoming request. Fires the <code>ActionEvent</code>.
|
||||||
|
* @param state the current page state
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void respond(PageState state) {
|
||||||
|
fireActionEvent(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the URL for a link and sets it as the "href" attribute
|
||||||
|
* of the parent.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @param parent the parent element
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void generateURL(PageState state, Element parent) {
|
||||||
|
setControlEvent(state);
|
||||||
|
try {
|
||||||
|
parent.addAttribute("href", state.stateAsURL());
|
||||||
|
} catch (IOException e) {
|
||||||
|
parent.addAttribute("href", "");
|
||||||
|
}
|
||||||
|
exportAttributes(parent);
|
||||||
|
state.clearControlEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the page state's control event. Should be overridden by child
|
||||||
|
* classes. By default, the link receives no control events whatsoever.
|
||||||
|
*
|
||||||
|
* @param ps the current page state
|
||||||
|
*/
|
||||||
|
// FIXME: Why is this not protected ?
|
||||||
|
public void setControlEvent(PageState ps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (Simple) Component with various descriptive information, specifically 'hints'
|
||||||
|
* with explanations about it's proper usage. These hints provide a kind of
|
||||||
|
* online manual.
|
||||||
|
*
|
||||||
|
* @author Peter Boy (pb@zes.uni-bremen.de)
|
||||||
|
* @version $Id: TextStylable.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
abstract public class DescriptiveComponent extends SimpleComponent {
|
||||||
|
|
||||||
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int the runtime environment
|
||||||
|
* and set com.arsdigita.bebop.DescriptiveComponent=DEBUG
|
||||||
|
* by uncommenting or adding the line. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(DescriptiveComponent.class);
|
||||||
|
|
||||||
|
/** Property to store informational text for the user about the Link, e.g.
|
||||||
|
* how to use it, or when to use it (or not to use it). */
|
||||||
|
private GlobalizedMessage m_hint; //= GlobalizationUtil.globalize("bebop.hint.no_entry_yet");
|
||||||
|
|
||||||
|
/** Property to store a (localized) label (or title) of this widget. A
|
||||||
|
* label is the text (name) displayed for the user to identify and
|
||||||
|
* distinguish the various elements on the screem. */
|
||||||
|
private GlobalizedMessage m_label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a popup hint for the component. It usually contains some explanation
|
||||||
|
* for the user about the component, how to use, why it is there, etc.
|
||||||
|
*
|
||||||
|
* @param hint GlobalizedMessage object with the information text.
|
||||||
|
*/
|
||||||
|
public void setHint(GlobalizedMessage hint) {
|
||||||
|
m_hint = hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the popup hint for the component. It is specifically meant for
|
||||||
|
* client classes which have to generate the xml on their own and can not
|
||||||
|
* use the generateDescriptionXML method provided.
|
||||||
|
*
|
||||||
|
* @return popup hint message for the component
|
||||||
|
*/
|
||||||
|
public GlobalizedMessage getHint() {
|
||||||
|
return m_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a popup hint for the Link. It usually contains some explanation for
|
||||||
|
* the user about the link, how to use, why it is there, etc.
|
||||||
|
*
|
||||||
|
* @param label GlobalizedMessage object with the text to identify and
|
||||||
|
* distinguish the component.
|
||||||
|
*/
|
||||||
|
public void setLabel(GlobalizedMessage label) {
|
||||||
|
m_label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the label for the component. It is specifically meant for
|
||||||
|
* client classes which have to generate the XML on their own and can not
|
||||||
|
* use the generateDescriptionXML method provided.
|
||||||
|
*
|
||||||
|
* @return popup hint message for the component
|
||||||
|
*/
|
||||||
|
public GlobalizedMessage getLabel() {
|
||||||
|
return m_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a (J)DOM fragment for clients to include into their generated
|
||||||
|
* XML.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param parent the XML Element instance to add the attributes managed by
|
||||||
|
* by this class
|
||||||
|
*/
|
||||||
|
protected void generateDescriptionXML(final PageState state,
|
||||||
|
final Element parent) {
|
||||||
|
|
||||||
|
if (m_label != null) {
|
||||||
|
parent.addAttribute("label", (String) m_label.localize());
|
||||||
|
}
|
||||||
|
if (m_hint != null) {
|
||||||
|
parent.addAttribute("hint", (String) m_hint.localize());
|
||||||
|
}
|
||||||
|
// Do we need this?
|
||||||
|
//exportAttributes(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,558 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.form.Hidden;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import com.arsdigita.bebop.util.Traversal;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.web.URL;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.web.RedirectSignal;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the visual structure of an HTML form. Forms can be constructed with a Container
|
||||||
|
* argument to specify the type of layout this form will adhere to. The default is a column panel.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* As an example, a form that accepts a first and last name may be set up as follows:
|
||||||
|
*
|
||||||
|
* <pre style="background: #cccccc">
|
||||||
|
* public class MyForm extends Form implements FormProcessListener {
|
||||||
|
*
|
||||||
|
* private Text m_firstName; private Text m_lastName;
|
||||||
|
*
|
||||||
|
* public MyForm() { super("myform"); add(new Label("First Name:")); m_firstName = new
|
||||||
|
* Text("firstName"); m_firstName.setDefaultValue("John"); add(m_firstName);
|
||||||
|
*
|
||||||
|
* add(new Label("Last Name:")); m_lastName = new Text("lastName");
|
||||||
|
* m_lastName.setDefaultValue("Doe"); m_lastName.addValidationListener(new NotNullValidationListener
|
||||||
|
* ("The last name")); add(m_lastName);
|
||||||
|
*
|
||||||
|
* add(new Submit("save", "Save")); addProcessListener(this); }
|
||||||
|
*
|
||||||
|
* public void process(FormSectionEvent e) { PageState s = e.getPageState();
|
||||||
|
*
|
||||||
|
* System.out.println("You are " + m_firstName.getValue(s) + " " + m_lastName.getValue(s)); } }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This form automatically checks that the user supplied a last name. Only then does it call the
|
||||||
|
* <code>process</code> method, which prints the user-supplied values.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id: Form.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class Form extends FormSection implements BebopConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal logger instance to faciliate debugging. Enable logging output by editing
|
||||||
|
* /WEB-INF/conf/log4j.properties int hte runtime environment and set
|
||||||
|
* com.arsdigita.bebop.Form=DEBUG by uncommenting or adding the line.
|
||||||
|
*/
|
||||||
|
private static final Logger s_log = Logger.getLogger(Form.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying a <code>get</code> submission method for this form. See the <a href=
|
||||||
|
* "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C HTML specification</a> for
|
||||||
|
* a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public final static String GET = "get";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying a <code>post</code> submission method for this form. See the <a href=
|
||||||
|
* "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C HTML specification</a> for
|
||||||
|
* a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public final static String POST = "post";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <code>name</code> attribute for the form.
|
||||||
|
*/
|
||||||
|
private final static String NAME = "name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the <code>method</code> attribute for the form.
|
||||||
|
*/
|
||||||
|
private final static String METHOD = "method";
|
||||||
|
|
||||||
|
private String m_action;
|
||||||
|
private boolean m_processInvisible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hold the FormData for one request.
|
||||||
|
*/
|
||||||
|
private RequestLocal m_formData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not a form is 'redirecting', meaning that it will clear the control
|
||||||
|
* event and redirect to the resulting state after form processing, so that a page reload won't
|
||||||
|
* cause the form to be resubmitted.
|
||||||
|
*/
|
||||||
|
private boolean m_isRedirecting = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form with the specified name. At the time of creation, instantiates a new
|
||||||
|
* form model for the form and instantiates a default ColumnPanel to contain the components.
|
||||||
|
*
|
||||||
|
* @param name the name of the form
|
||||||
|
*/
|
||||||
|
public Form(String name) {
|
||||||
|
this(name, new GridPanel(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form with the specified name and container. At the time of creation,
|
||||||
|
* instantiates a new form model for the form and replaces the default ColumnPanel with the
|
||||||
|
* specified container as the implicit container of the components.
|
||||||
|
*
|
||||||
|
* @param name the name attribute of the form
|
||||||
|
* @param panel the implicit container that will hold the components
|
||||||
|
*/
|
||||||
|
public Form(String name, Container panel) {
|
||||||
|
super(panel, new FormModel(name));
|
||||||
|
initFormData();
|
||||||
|
setName(name);
|
||||||
|
setProcessInvisible(false);
|
||||||
|
addMagicTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the output to a DOM to be used with the XSLT template to produce the appropriate
|
||||||
|
* output. If the form is not visible, no output is generated.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Generates a DOM fragment:
|
||||||
|
* <p>
|
||||||
|
* <code><pre>
|
||||||
|
* <bebop:form action=%url; %bebopAttr;>
|
||||||
|
* .. XML for panel ..
|
||||||
|
* .. XML for page state ..
|
||||||
|
* </bebop:form>
|
||||||
|
* </pre></code>
|
||||||
|
*
|
||||||
|
* @param s the page state used to determine the values of form widgets and page state
|
||||||
|
* attributes
|
||||||
|
* @param parent the XML element to which the form adds its XML representation
|
||||||
|
*
|
||||||
|
* @see PageState#generateXML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState s, Element parent) {
|
||||||
|
if (isVisible(s)) {
|
||||||
|
Element form = generateXMLSansState(s, parent);
|
||||||
|
|
||||||
|
s.setControlEvent(this);
|
||||||
|
s.generateXML(form, getModel().getParametersToExclude());
|
||||||
|
s.clearControlEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML representing the form and its widgets, but not the state information from
|
||||||
|
* <code>s</code>.
|
||||||
|
*
|
||||||
|
* @param s represents the curent request
|
||||||
|
* @param parent
|
||||||
|
*
|
||||||
|
* @return the top-level element for the form
|
||||||
|
*/
|
||||||
|
protected Element generateXMLSansState(PageState s, Element parent) {
|
||||||
|
Element form = parent.newChildElement("bebop:form", BEBOP_XML_NS);
|
||||||
|
|
||||||
|
// Encode the URL with the servlet session information;
|
||||||
|
// do not use DispatcherHelper.encodeURL because the
|
||||||
|
// ACS global parameters are provided via the FormData.
|
||||||
|
String url = null;
|
||||||
|
|
||||||
|
if (m_action == null) {
|
||||||
|
final URL requestURL = Web.getWebContext().getRequestURL();
|
||||||
|
|
||||||
|
if (requestURL == null) {
|
||||||
|
url = s.getRequest().getRequestURI();
|
||||||
|
} else {
|
||||||
|
url = requestURL.getRequestURI();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url = m_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addAttribute("action", s.getResponse().encodeURL(url));
|
||||||
|
|
||||||
|
exportAttributes(form);
|
||||||
|
|
||||||
|
m_panel.generateXML(s, form);
|
||||||
|
|
||||||
|
generateErrors(s, form);
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
protected void generateErrors(PageState ps, Element parent) {
|
||||||
|
|
||||||
|
for (Iterator it = getFormData(ps).getErrors(); it.hasNext();) {
|
||||||
|
Element errors = parent.newChildElement(BEBOP_FORMERRORS,
|
||||||
|
BEBOP_XML_NS);
|
||||||
|
Object msg = it.next();
|
||||||
|
|
||||||
|
if (msg == null) {
|
||||||
|
errors.addAttribute("message", "Unknown error");
|
||||||
|
} else {
|
||||||
|
errors.addAttribute("message",
|
||||||
|
(String) ((GlobalizedMessage) msg).localize(ps.getRequest()));
|
||||||
|
}
|
||||||
|
errors.addAttribute("id", getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Determine whether or not this Form will redirect after its process listeners are fired.</p>
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isRedirecting() {
|
||||||
|
return m_isRedirecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting the redirecting flag will cause the Form to clear the control event and redirect back
|
||||||
|
* to the current URL, after firing all process listeners. Doing so means that a user reload
|
||||||
|
* will not cause the form to be resubmitted. The default value for this flag is false.
|
||||||
|
*
|
||||||
|
* @param isRedirecting
|
||||||
|
*/
|
||||||
|
public void setRedirecting(boolean isRedirecting) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_isRedirecting = isRedirecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to the request by processing this form with the HTTP request given in
|
||||||
|
* <code>state</code>.
|
||||||
|
*
|
||||||
|
* @see #process process(...)
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
*
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void respond(PageState state) throws ServletException {
|
||||||
|
final FormData data = process(state);
|
||||||
|
|
||||||
|
if (m_isRedirecting && data.isValid()) {
|
||||||
|
state.clearControlEvent();
|
||||||
|
|
||||||
|
throw new RedirectSignal(state.toURL(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
// Methods to set the HTML attributes of the FORM element
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
/**
|
||||||
|
* Sets the <code>name</code> attribute for the form.
|
||||||
|
*
|
||||||
|
* @param name the name for the form
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(NAME, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the <code>name</code> attribute for this form.
|
||||||
|
*
|
||||||
|
* @return the name for this form.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return (String) getAttribute(NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <code>enctype</code> attribute used in the <code>form</code> element. No encoding
|
||||||
|
* type is specified by default.
|
||||||
|
*
|
||||||
|
* @param encType the encoding type
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setEncType(String encType) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("enctype", encType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <code>onSubmit</code> attribute used in the <code>form</code> element. No onsubmit
|
||||||
|
* handler is specified by default.
|
||||||
|
*
|
||||||
|
* @param javascriptCode the javascript code associated with this attribute
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setOnSubmit(String javascriptCode) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("onSubmit", javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <code>ONRESET</code> attribute used in the <code>FORM</code> element. No onreset
|
||||||
|
* handler is specified by default.
|
||||||
|
*
|
||||||
|
* @param javascriptCode the javascript code associated with this attribute
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setOnReset(String javascriptCode) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("onReset", javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the HTTP method used to submit the form.
|
||||||
|
*
|
||||||
|
* @param method either <code>GET</code> or <code>POST</code>
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setMethod(String method) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(METHOD, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMethod() {
|
||||||
|
return getAttribute(METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if form processing is turned on when the form is invisible.
|
||||||
|
*
|
||||||
|
* @return true if the form listeners should be processed even when the form is not visible on
|
||||||
|
* the page, false otherwise
|
||||||
|
*/
|
||||||
|
protected boolean getProcessInvisible() {
|
||||||
|
return m_processInvisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns form processing on/off when the form is invisible.
|
||||||
|
*
|
||||||
|
* @param processInvisible true if the form listeners should be processed even when the form is
|
||||||
|
* not visible on the page
|
||||||
|
*/
|
||||||
|
protected void setProcessInvisible(boolean processInvisible) {
|
||||||
|
m_processInvisible = processInvisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the URL for the form's <code>action</code> attribute. This is the URL to which
|
||||||
|
* submissions will be sent when the user clicks a submit button on the form. By default, the
|
||||||
|
* action is <code>null</code>, instructing the form to set the action to the URL of the page in
|
||||||
|
* which it is used. If the action is set to a different URL, none of the listeners registered
|
||||||
|
* with this form will be run.
|
||||||
|
*
|
||||||
|
* @param action the URL to submit this form to
|
||||||
|
*
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setAction(String action) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL for the form's <code>action</code> attribute.
|
||||||
|
*
|
||||||
|
* @return the URL to which to submit this form.
|
||||||
|
*
|
||||||
|
* @see #setAction setAction
|
||||||
|
*/
|
||||||
|
public final String getAction() {
|
||||||
|
return m_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes this form, creating a <code>FormData</code> object. Runs the right set of init,
|
||||||
|
* validation, and process listeners, depending on whether this is an initial request to the
|
||||||
|
* form and whether the form submission was valid. Submission listeners are always run.
|
||||||
|
*
|
||||||
|
* @see #getFormData
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
*
|
||||||
|
* @return the values extracted from the HTTP request contained in <code>state</code>.
|
||||||
|
*
|
||||||
|
* @throws com.arsdigita.bebop.FormProcessException
|
||||||
|
* @pre state != null
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public FormData process(PageState state) throws FormProcessException {
|
||||||
|
Assert.exists(state, "PageState");
|
||||||
|
FormData result = new FormData(getModel(), state.getRequest());
|
||||||
|
setFormData(state, result);
|
||||||
|
|
||||||
|
// Unless invisible form processing is turned on, don't run any
|
||||||
|
// listeners if this form is not visible.
|
||||||
|
if (getProcessInvisible() || state.isVisibleOnPage(this)) {
|
||||||
|
getModel().process(state, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the form data constructed by the {@link #process
|
||||||
|
* process} method for the request described by <code>state</code>. Processes the form if it has
|
||||||
|
* not already been processed.
|
||||||
|
*
|
||||||
|
* @param state describes the current request
|
||||||
|
*
|
||||||
|
* @return the values extracted from the HTTP request contained in <code>state</code>, or
|
||||||
|
* <code>null</code> if the form has not been processed yet.
|
||||||
|
*
|
||||||
|
* @pre state != null
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public FormData getFormData(PageState state) {
|
||||||
|
return (FormData) m_formData.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Hidden Tag to this form so that our controller can determine if this is an initial
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
protected void addMagicTag() {
|
||||||
|
Hidden h = new Hidden(getModel().getMagicTagName());
|
||||||
|
h.setDefaultValue("visited");
|
||||||
|
add(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the components contained in this form, collecting parameterModels and Listeners
|
||||||
|
* into this form's FormModel.
|
||||||
|
*/
|
||||||
|
protected void traverse() {
|
||||||
|
Traversal formRegistrar = new Traversal() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void act(Component c) {
|
||||||
|
if (c == Form.this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c instanceof Form) {
|
||||||
|
throw new IllegalStateException("Forms cannot contain other Forms");
|
||||||
|
}
|
||||||
|
c.register(Form.this, getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
formRegistrar.preorder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds this form to the page and traverses the components contained in this form, collecting
|
||||||
|
* parameterModels and Listeners into this form's FormModel.
|
||||||
|
*
|
||||||
|
* @param p page in which to register this form
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void register(Page p) {
|
||||||
|
traverse();
|
||||||
|
p.addComponent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public void excludeParameterFromExport(ParameterModel model) {
|
||||||
|
getModel().excludeFormParameterFromExport(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize <code>m_formData</code> so that accessing the per-request form data forces the
|
||||||
|
* form to be processed on the first access and caches the form data for subsequent requests.
|
||||||
|
*/
|
||||||
|
private void initFormData() {
|
||||||
|
m_formData = new RequestLocal() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object initialValue(PageState s) {
|
||||||
|
// TODO: We need to come up with the right strategy for
|
||||||
|
// how we deal with FormProcessExceptions. Are they fatal
|
||||||
|
// ? Do we just add them to the form validation errors ?
|
||||||
|
try {
|
||||||
|
return process(s);
|
||||||
|
} catch (FormProcessException e) {
|
||||||
|
s_log.error("Form Process exception", e);
|
||||||
|
throw new UncheckedWrapperException("Form Process error: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to a String.
|
||||||
|
*
|
||||||
|
* @return a human-readable representation of <code>this</code>.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " " + "[" + getName() + "," + getAction() + "," + getMethod()
|
||||||
|
+ "," + isRedirecting() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected access to set the formdata request local. This method is required if a subclass
|
||||||
|
* wishes to override the process method.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
protected void setFormData(PageState state, FormData data) {
|
||||||
|
m_formData.set(state, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,832 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.util.URLRewriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the data associated with forms and other remote sources.
|
||||||
|
|
||||||
|
* <p>The basic task of a <code>FormData</code> object is to transform
|
||||||
|
* a set of key-value string pairs into a validated set of Java data
|
||||||
|
* objects for use in subsequent processing. In most cases the original
|
||||||
|
* data is an HTTP request.
|
||||||
|
|
||||||
|
* <p>To perform the transformation, a separate instance of
|
||||||
|
* <code>FormModel</code> is used to specify the name and basic data
|
||||||
|
* type of each expected parameter in the set, as well as any
|
||||||
|
* additional validation steps required. The <code>FormData</code>
|
||||||
|
* stores both the transformed data objects and any validation
|
||||||
|
* error messages associated with an individual parameter or the
|
||||||
|
* form as a whole. Once the data has been validated, individual data
|
||||||
|
* objects may be queried from a <code>FormData</code> using the
|
||||||
|
* standard <code>get</code> method of the <code>Map</code> interface.
|
||||||
|
*
|
||||||
|
* <p><code>FormData</code> objects may also be used to control the
|
||||||
|
* entire lifecycle of self-validating forms, which report errors to
|
||||||
|
* the user in the context of the form itself, rather than on a
|
||||||
|
* separate page.
|
||||||
|
*
|
||||||
|
* <p>See the Forms API Developer Guide for details on using the
|
||||||
|
* <code>FormData</code> class.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @version $Id: FormData.java 287 2005-02-22 00:29:02Z sskracic $ */
|
||||||
|
|
||||||
|
public class FormData implements Map, Cloneable {
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap m_parameterDataValues = new HashMap();
|
||||||
|
private LinkedList m_formErrors;
|
||||||
|
private FormModel m_model;
|
||||||
|
|
||||||
|
private Locale m_locale;
|
||||||
|
private boolean m_isTransformed;
|
||||||
|
private boolean m_isValid;
|
||||||
|
|
||||||
|
private boolean m_isSubmission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that no one can create this object from outside the package
|
||||||
|
* without supplying meaningful parameters
|
||||||
|
*/
|
||||||
|
private FormData() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
*
|
||||||
|
* @param model a <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request
|
||||||
|
*
|
||||||
|
* @param request an HTTP request object passed from the servlet
|
||||||
|
* container
|
||||||
|
*
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public FormData(FormModel model, HttpServletRequest request)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
this(model, request, Locale.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
*
|
||||||
|
* @param model a <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request
|
||||||
|
*
|
||||||
|
* @param request an HTTP request object passed from the servlet
|
||||||
|
* container
|
||||||
|
*
|
||||||
|
* @param isSubmission <code>true</code> if the request should be treated
|
||||||
|
* as a form submission by the user
|
||||||
|
*
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs.
|
||||||
|
*/
|
||||||
|
public FormData(FormModel model, HttpServletRequest request,
|
||||||
|
boolean isSubmission)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
this(model, request, Locale.getDefault(), isSubmission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
*
|
||||||
|
* @param model a <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request
|
||||||
|
*
|
||||||
|
* @param request an HTTP request object passed from the servlet
|
||||||
|
* container
|
||||||
|
*
|
||||||
|
* @param isSubmission <code>true</code> if the request should be treated
|
||||||
|
* as a form submission by the user
|
||||||
|
*
|
||||||
|
* @param fallback a fallback FormData object. If a value for
|
||||||
|
* a parameter in the form model <code>model</code> is not in
|
||||||
|
* the current request parameters, the <code>fallback</code> object is
|
||||||
|
* searched.
|
||||||
|
*
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs
|
||||||
|
*/
|
||||||
|
public FormData(FormModel model, HttpServletRequest request,
|
||||||
|
boolean isSubmission, FormData fallback)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
this(model, request, Locale.getDefault(), isSubmission, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
* Error messages are provided in the specified locale.
|
||||||
|
*
|
||||||
|
* @param model A <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request.
|
||||||
|
*
|
||||||
|
* @param request An HTTP request object passed from the servlet
|
||||||
|
* container.
|
||||||
|
*
|
||||||
|
* @param locale The locale for which all error messages will be
|
||||||
|
* prepared. This may be used in a multilingual environment to
|
||||||
|
* tailor the output to the preferences or geographic location of
|
||||||
|
* individual users on a per-request basis.
|
||||||
|
*
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
* @pre locale != null
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs
|
||||||
|
*/
|
||||||
|
public FormData(FormModel model, HttpServletRequest request, Locale locale)
|
||||||
|
throws FormProcessException {
|
||||||
|
this(model, request, locale,
|
||||||
|
request.getParameter(model.getMagicTagName()) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
* Error messages are provided in the specified locale.
|
||||||
|
*
|
||||||
|
* @param model A <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request.
|
||||||
|
*
|
||||||
|
* @param request An HTTP request object passed from the servlet
|
||||||
|
* container.
|
||||||
|
*
|
||||||
|
* @param locale The locale for which all error messages will be
|
||||||
|
* prepared. This may be used in a multilingual environment to
|
||||||
|
* tailor the output to the preferences or geographic location of
|
||||||
|
* individual users on a per-request basis.
|
||||||
|
*
|
||||||
|
* @param isSubmission <code>true</code> if the request should be treated
|
||||||
|
* as a form submission by the user.
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
* @pre locale != null
|
||||||
|
*/
|
||||||
|
public FormData(FormModel model, HttpServletRequest request,
|
||||||
|
Locale locale, boolean isSubmission)
|
||||||
|
throws FormProcessException {
|
||||||
|
this(model, request, locale, isSubmission, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>FormData</code> object containing the
|
||||||
|
* transformed and validated query parameters from an HTTP request.
|
||||||
|
* Error messages are provided in the specified locale.
|
||||||
|
*
|
||||||
|
* @param model A <code>FormModel</code> describing the parameters
|
||||||
|
* and validation constraints for this request.
|
||||||
|
*
|
||||||
|
* @param request An HTTP request object passed from the servlet
|
||||||
|
* container.
|
||||||
|
*
|
||||||
|
* @param locale The locale for which all error messages will be
|
||||||
|
* prepared. This may be used in a multilingual environment to
|
||||||
|
* tailor the output to the preferences or geographic location of
|
||||||
|
* individual users on a per-request basis.
|
||||||
|
*
|
||||||
|
* @param isSubmission <code>true</code> if the request should be treated
|
||||||
|
* as a form submission by the user.
|
||||||
|
*
|
||||||
|
* @param fallback a fallback FormData object. If a value for
|
||||||
|
* a parameter in the form model <code>model</code> is not in
|
||||||
|
* the current request parameters, the <code>fallback</code> object is
|
||||||
|
* searched.
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs
|
||||||
|
* @pre model != null
|
||||||
|
* @pre request != null
|
||||||
|
* @pre locale != null
|
||||||
|
*/
|
||||||
|
public FormData(FormModel model, HttpServletRequest request,
|
||||||
|
Locale locale, boolean isSubmission,
|
||||||
|
FormData fallback)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
Assert.exists(model, "FormModel");
|
||||||
|
Assert.exists(request, "HttpServletRequest");
|
||||||
|
Assert.exists(locale, "Locale");
|
||||||
|
|
||||||
|
m_locale = locale;
|
||||||
|
m_model = model;
|
||||||
|
m_isTransformed = false;
|
||||||
|
|
||||||
|
m_isSubmission = isSubmission;
|
||||||
|
m_isValid = m_isSubmission;
|
||||||
|
|
||||||
|
createParameterData(request, fallback);
|
||||||
|
|
||||||
|
Iterator params = URLRewriter.getGlobalParams(request).iterator();
|
||||||
|
while (params.hasNext()) {
|
||||||
|
ParameterData param = (ParameterData)params.next();
|
||||||
|
setParameter(param.getModel().getName(), param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates this <code>FormData</code> object according to its form model.
|
||||||
|
* If the <code>FormData</code> is already valid, does nothing.
|
||||||
|
*
|
||||||
|
* @param state describes the current page state
|
||||||
|
* @pre state != null
|
||||||
|
*/
|
||||||
|
public void validate(PageState state) {
|
||||||
|
|
||||||
|
if (isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isValid = true;
|
||||||
|
|
||||||
|
if (m_formErrors != null) {
|
||||||
|
m_formErrors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model.validate(state, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates this <code>FormData</code> object against its form model,
|
||||||
|
* regardless of whether the object is currently valid.
|
||||||
|
*
|
||||||
|
* @param state describes the current page state
|
||||||
|
* @pre state != null
|
||||||
|
*/
|
||||||
|
public void forceValidate(PageState state) {
|
||||||
|
invalidate();
|
||||||
|
validate(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports a validation error on the form as a whole.
|
||||||
|
*
|
||||||
|
* @param message a String of the error message
|
||||||
|
* @pre message != null
|
||||||
|
* @deprecated refactor and use addError(GlobalizedMessage) instead
|
||||||
|
*/
|
||||||
|
public void addError(String message) {
|
||||||
|
addError(new GlobalizedMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports a validation error on the form as a whole.
|
||||||
|
* Uses a GlobalizedMessage for inklusion
|
||||||
|
*
|
||||||
|
* @param message the error message
|
||||||
|
* @pre message != null
|
||||||
|
*/
|
||||||
|
public void addError(GlobalizedMessage message) {
|
||||||
|
|
||||||
|
if (m_formErrors == null) {
|
||||||
|
m_formErrors = new LinkedList();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_formErrors.add(message);
|
||||||
|
m_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error message to the ParameterData object associated with
|
||||||
|
* the parameter model identified by <code>name</code>.
|
||||||
|
*
|
||||||
|
* @param name the name of the parameter model to whose
|
||||||
|
* ParameterData the error message will be added
|
||||||
|
*
|
||||||
|
* @param message the text of the error message to add
|
||||||
|
*
|
||||||
|
* @pre name != null
|
||||||
|
* @pre message != null
|
||||||
|
* @deprecated use addError(String name, GlobalizedMessage message) instead
|
||||||
|
*/
|
||||||
|
public void addError(String name, String message) {
|
||||||
|
|
||||||
|
ParameterData parameter;
|
||||||
|
|
||||||
|
if (!m_parameterDataValues.containsKey(name)) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Attempt to set Error in Non-Existant ParameterData");
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter = (ParameterData) m_parameterDataValues.get(name);
|
||||||
|
parameter.addError(message);
|
||||||
|
m_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error message to the ParameterData object associated with
|
||||||
|
* the parameter model identified by <code>name</code>.
|
||||||
|
*
|
||||||
|
* @param name the name of the parameter model to whose
|
||||||
|
* ParameterData the error message will be added
|
||||||
|
*
|
||||||
|
* @param message the text of the error message to add
|
||||||
|
*
|
||||||
|
* @pre name != null
|
||||||
|
* @pre message != null
|
||||||
|
*/
|
||||||
|
public void addError(String name, GlobalizedMessage message) {
|
||||||
|
|
||||||
|
ParameterData parameter;
|
||||||
|
|
||||||
|
if (!m_parameterDataValues.containsKey(name)) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Attempt to set Error in Non-Existant ParameterData");
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter = (ParameterData) m_parameterDataValues.get(name);
|
||||||
|
parameter.addError(message);
|
||||||
|
m_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the errors associated with the specified parameter.
|
||||||
|
*
|
||||||
|
* @param name the name of the parameter whose errors we are interested in
|
||||||
|
*
|
||||||
|
* @return an iterator of errors. Each error is just a string for
|
||||||
|
* now.
|
||||||
|
*
|
||||||
|
* @pre name != null
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public Iterator getErrors(String name) {
|
||||||
|
|
||||||
|
ParameterData parameter
|
||||||
|
= (ParameterData)m_parameterDataValues.get(name);
|
||||||
|
|
||||||
|
if (parameter == null) {
|
||||||
|
return Collections.EMPTY_LIST.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameter.getErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over all the errors on this form that are not
|
||||||
|
* associated with any particular parameter. Such errors may have
|
||||||
|
* been generated by a FormValidationListener.
|
||||||
|
*
|
||||||
|
* @return an iterator over error messages.
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public Iterator getErrors() {
|
||||||
|
|
||||||
|
if (m_formErrors == null) {
|
||||||
|
return Collections.EMPTY_LIST.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_formErrors.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over all of the errors on this form.
|
||||||
|
* This includes both errors associated with particular parameters
|
||||||
|
* and errors associated with the form as a whole.
|
||||||
|
*
|
||||||
|
* @return an iterator over all error messages.
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public Iterator getAllErrors() {
|
||||||
|
|
||||||
|
return new Iterator() {
|
||||||
|
|
||||||
|
private Iterator params, paramErrors, formErrors;
|
||||||
|
|
||||||
|
{
|
||||||
|
params = m_parameterDataValues.values().iterator();
|
||||||
|
paramErrors = Collections.EMPTY_LIST.iterator();
|
||||||
|
formErrors = getErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seekToNextError() {
|
||||||
|
while (! paramErrors.hasNext() && params.hasNext()) {
|
||||||
|
paramErrors
|
||||||
|
= ((ParameterData)params.next()).getErrors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
seekToNextError();
|
||||||
|
return paramErrors.hasNext() || formErrors.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object next() throws NoSuchElementException {
|
||||||
|
|
||||||
|
seekToNextError();
|
||||||
|
if (paramErrors.hasNext()) {
|
||||||
|
return paramErrors.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// An error will be thrown if !formErrors.hasNext()
|
||||||
|
return formErrors.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the specified ParameterData object.
|
||||||
|
*
|
||||||
|
* @param name the name of the parameterModel to retrieve
|
||||||
|
* @return the parameter data object specified.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ParameterData getParameter(String name) {
|
||||||
|
return (ParameterData)m_parameterDataValues.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ParameterData object identified by the name in this FormData
|
||||||
|
* Object.
|
||||||
|
*
|
||||||
|
* @param name the name of the parameterModel
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void setParameter(String name, ParameterData value) {
|
||||||
|
m_parameterDataValues.put(name,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of all the <code>ParameterData</code> objects.
|
||||||
|
*
|
||||||
|
* @return a collection of all the <code>ParameterData</code> objects.
|
||||||
|
*/
|
||||||
|
public final Collection getParameters() {
|
||||||
|
return m_parameterDataValues.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether this request represents a submission event.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this request represents a submission event;
|
||||||
|
* <code>false</code> if it represents an initialization event.
|
||||||
|
*/
|
||||||
|
public final boolean isSubmission() {
|
||||||
|
return m_isSubmission;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the key-value string pairs in the
|
||||||
|
* request have been transformed into Java data objects.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the key-value string pairs have been
|
||||||
|
* transformed into Java data objects;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final boolean isTransformed() {
|
||||||
|
return m_isTransformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether any errors were found during validation of
|
||||||
|
* a form submission.
|
||||||
|
* @return <code>true</code> if no errors were found; <code>false</code>
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final boolean isValid() {
|
||||||
|
return m_isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets isValid to <code>false</code>. We do not allow programmers
|
||||||
|
* to manually toggle the isValid value to <code>true</code>.
|
||||||
|
* Hence this method takes no
|
||||||
|
* arguments and only sets isValid flag to false
|
||||||
|
* @deprecated Use invalidate() instead
|
||||||
|
*/
|
||||||
|
public void setInvalid() {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set isValid to <code>false</code>. We do not allow programmers
|
||||||
|
* to manually toggle the isValid value to <code>true</code>.
|
||||||
|
*/
|
||||||
|
public final void invalidate() {
|
||||||
|
m_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- private helper methods to initialize object ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of a parameter within the associated ParameterData
|
||||||
|
* object
|
||||||
|
*
|
||||||
|
* @param name Name of the parameterModel whose ParameterData object
|
||||||
|
* we are setting
|
||||||
|
*
|
||||||
|
* @param value Value to assign the ParmeterData object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void setParameterValue(String name, Object value) {
|
||||||
|
ParameterData parameter = (ParameterData) m_parameterDataValues.get(name);
|
||||||
|
if (parameter != null) {
|
||||||
|
parameter.setValue(value);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Parameter " + name +
|
||||||
|
" does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate through parameterModels extracting values from the
|
||||||
|
* request, and transforming the value according to the parameter
|
||||||
|
* model This code incorporates
|
||||||
|
* ParameterModel.createParameterData(request)
|
||||||
|
*
|
||||||
|
* @param request the HttpServletRequest
|
||||||
|
* @param fallback a fallback FormData object. If any parameter
|
||||||
|
* in the form model does not have a value in the request,
|
||||||
|
* try to locate its value in the fallback object.
|
||||||
|
*/
|
||||||
|
private void createParameterData(HttpServletRequest request,
|
||||||
|
FormData fallback)
|
||||||
|
throws FormProcessException {
|
||||||
|
ParameterModel parameterModel;
|
||||||
|
ParameterData parameterData;
|
||||||
|
Iterator parameters = m_model.getParameters();
|
||||||
|
|
||||||
|
while (parameters.hasNext()) {
|
||||||
|
parameterModel = (ParameterModel) parameters.next();
|
||||||
|
|
||||||
|
// createParamterData automagically handles default values
|
||||||
|
// and errors in tranformation.
|
||||||
|
|
||||||
|
Object defaultValue = null;
|
||||||
|
if (fallback != null) {
|
||||||
|
parameterData =
|
||||||
|
fallback.getParameter(parameterModel.getName());
|
||||||
|
if (parameterData != null) {
|
||||||
|
defaultValue = parameterData.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify a default from the fallback
|
||||||
|
parameterData =
|
||||||
|
parameterModel.createParameterData(request,
|
||||||
|
defaultValue,
|
||||||
|
isSubmission());
|
||||||
|
Assert.exists(parameterData);
|
||||||
|
setParameter(parameterModel.getName(), parameterData);
|
||||||
|
}
|
||||||
|
m_isTransformed=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Public methods to satisfy Map interface ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return m_parameterDataValues.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
// this is very expensive with ParameterData
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is just plain wrong. Either you pretend to be a Map of
|
||||||
|
* things, or you are a Map of ParameterData-s.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set entrySet() {
|
||||||
|
return m_parameterDataValues.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value contained by the ParameterData object named
|
||||||
|
* by <code>key</code>.
|
||||||
|
* If no key is found, throws IllegalArgumentException.
|
||||||
|
* @param key the parameter data object to retrieve
|
||||||
|
* @return the value in the specified parameter data object.
|
||||||
|
* @throws java.lang.IllegalArgumentException thrown when the key
|
||||||
|
* is not a valid parameter.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object get(Object key) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
ParameterData p = getParameter((String)key);
|
||||||
|
if (p != null) {
|
||||||
|
return p.getValue();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("parameter " + key +
|
||||||
|
" not part of the form model");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param m
|
||||||
|
* @return
|
||||||
|
* @deprecated Use get(m.getName()) instead, and then manually check
|
||||||
|
* for model identity
|
||||||
|
*/
|
||||||
|
public Object get(ParameterModel m) {
|
||||||
|
ParameterData p = getParameter(m.getName());
|
||||||
|
|
||||||
|
return ( p.getModel() == m ) ? p : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a date object for the specified parameter name.
|
||||||
|
* @param key the object to retrieve
|
||||||
|
* @return a date object for the specified parameter name.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Date getDate(Object key) {
|
||||||
|
return (Date) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an integer object for the specified parameter name.
|
||||||
|
* @param key the object to retrieve
|
||||||
|
* @return an integer object for the specified parameter name.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public Integer getInteger(Object key) {
|
||||||
|
return (Integer) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a String object for the specified parameter name.
|
||||||
|
* @param key the object to retrieve
|
||||||
|
* @return a string object for the specified parameter name.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public String getString(Object key) {
|
||||||
|
return (String) get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return m_parameterDataValues.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set keySet() {
|
||||||
|
return m_parameterDataValues.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object put(Object key, Object value) {
|
||||||
|
Object previousValue = get(key);
|
||||||
|
setParameterValue((String)key, value);
|
||||||
|
m_isValid = false;
|
||||||
|
return previousValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map t) {
|
||||||
|
for (Iterator i = t.keySet().iterator(); i.hasNext(); ) {
|
||||||
|
String key = (String) i.next();
|
||||||
|
setParameterValue(key, t.get(key));
|
||||||
|
}
|
||||||
|
m_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object remove(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return m_parameterDataValues.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection values() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws CloneNotSupportedException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
FormData result = (FormData) super.clone();
|
||||||
|
result.m_parameterDataValues = new HashMap();
|
||||||
|
for (Iterator i= m_parameterDataValues.keySet().iterator();
|
||||||
|
i.hasNext(); ) {
|
||||||
|
Object key = i.next();
|
||||||
|
ParameterData val = (ParameterData) m_parameterDataValues.get(key);
|
||||||
|
result.m_parameterDataValues.put(key, val.clone());
|
||||||
|
}
|
||||||
|
if (m_formErrors != null) {
|
||||||
|
result.m_formErrors = (LinkedList) m_formErrors.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
|
||||||
|
for (Iterator i = getAllErrors(); i.hasNext();) {
|
||||||
|
s.append(i.next()).append(System.getProperty("line.separator"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to a String.
|
||||||
|
* The method {@link #toString()} returns all errors.
|
||||||
|
*
|
||||||
|
* @return a human-readable representation of <code>this</code>.
|
||||||
|
*/
|
||||||
|
public String asString() {
|
||||||
|
String newLine = System.getProperty("line.separator");
|
||||||
|
StringBuilder to = new StringBuilder();
|
||||||
|
to.append(super.toString() + " = {" + newLine);
|
||||||
|
//Map
|
||||||
|
to.append("m_parameterDataValues = ")
|
||||||
|
.append(m_parameterDataValues).append(",").append(newLine);
|
||||||
|
//LinkedList
|
||||||
|
to.append("m_formErrors = " + m_formErrors + "," + newLine);
|
||||||
|
//FormModel
|
||||||
|
to.append("m_model = " + m_model + "," + newLine);
|
||||||
|
to.append("m_locale = " + m_locale + "," + newLine);
|
||||||
|
to.append("m_isTransformed = " + m_isTransformed + "," + newLine);
|
||||||
|
to.append("m_isValid = " + m_isValid + "," + newLine);
|
||||||
|
to.append("m_isSubmission = " + m_isSubmission + newLine);
|
||||||
|
to.append("}");
|
||||||
|
return to.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,550 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.EventListenerList;
|
||||||
|
import com.arsdigita.bebop.event.FormInitListener;
|
||||||
|
import com.arsdigita.bebop.event.FormProcessListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.event.FormSubmissionListener;
|
||||||
|
import com.arsdigita.bebop.event.FormValidationListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.Lockable;
|
||||||
|
import com.arsdigita.util.URLRewriter;
|
||||||
|
import com.arsdigita.web.RedirectSignal;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for two classes of
|
||||||
|
* objects: <tt>ParameterModels</tt> and <tt>ValidationListeners</tt>.</p>
|
||||||
|
* <ul>
|
||||||
|
* <li><tt>ParameterModels</tt> are associated
|
||||||
|
* with the data objects that the user submits with the form.</li>
|
||||||
|
* <li><tt>ValidationListeners</tt> provide custom
|
||||||
|
* cross-checking of parameter values.</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>Instances of this class provide a specification for transforming a
|
||||||
|
* set of key-value string pairs into a set of validated Java data
|
||||||
|
* objects.
|
||||||
|
* A single instance of this
|
||||||
|
* class can handle all submissions to a particular form.
|
||||||
|
* <p>The most common usage for this class is
|
||||||
|
* is to use a private variable in a servlet to store the
|
||||||
|
* model, and to construct it in the servlet <code>init</code> method.
|
||||||
|
* That way, the model persists for the lifetime of the servlet, reducing
|
||||||
|
* the memory and processing overhead for each request.
|
||||||
|
* <p>See the
|
||||||
|
* Forms API Developer Guide for details on using the
|
||||||
|
* <code>FormModel</code> class.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @version $Id: FormModel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class FormModel implements Lockable {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(FormModel.class);
|
||||||
|
|
||||||
|
private static final String MAGIC_TAG_PREFIX = "form.";
|
||||||
|
|
||||||
|
private String m_name = null;
|
||||||
|
private List m_parameterModels = null;
|
||||||
|
private List m_parametersToExclude = null;
|
||||||
|
private boolean m_locked = false;
|
||||||
|
private boolean m_defaultOverridesNull;
|
||||||
|
|
||||||
|
protected EventListenerList m_listenerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form model.
|
||||||
|
*
|
||||||
|
* @param name a URL-encoded keyword used to identify this form model
|
||||||
|
* */
|
||||||
|
public FormModel(String name) {
|
||||||
|
this(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new form model. The <code>defaultOverridesNull</code>
|
||||||
|
* parameter is passed on to all parameter models that are added to the
|
||||||
|
* form model. If it is <code>true</code>, the parameter model will use
|
||||||
|
* the default value whenever it would normally set the parameter's value
|
||||||
|
* to null, for example if the parameter is missing from the request. If
|
||||||
|
* this value is <code>false</code>, the default parameter value will
|
||||||
|
* only be used if the request being processed is not a submission, but
|
||||||
|
* an initial request for the form model.
|
||||||
|
*
|
||||||
|
* <p> This method is only package-friendly since it is only useful to
|
||||||
|
* the Page class. Everybody else should be happy with the public
|
||||||
|
* constructor.
|
||||||
|
*
|
||||||
|
* @param name a URL-encoded keyword used to identify this form model
|
||||||
|
*
|
||||||
|
* @param defaultOverridesNull <code>true</code> if the default value for
|
||||||
|
* parameters should be used whenever the value would be
|
||||||
|
* <code>null</code> ordinarily.
|
||||||
|
*/
|
||||||
|
FormModel(String name, boolean defaultOverridesNull) {
|
||||||
|
Assert.exists(name, "Name");
|
||||||
|
m_parameterModels = new LinkedList();
|
||||||
|
m_parametersToExclude = new LinkedList();
|
||||||
|
m_listenerList = new EventListenerList();
|
||||||
|
m_name = name;
|
||||||
|
m_defaultOverridesNull = defaultOverridesNull;
|
||||||
|
m_parameterModels.addAll(URLRewriter.getGlobalModels());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this form model.
|
||||||
|
*
|
||||||
|
* @return a URL-encoded keyword used to identify requests
|
||||||
|
* conforming to this form model.
|
||||||
|
* */
|
||||||
|
public final String getName() {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setName(String name) {
|
||||||
|
m_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getMagicTagName() {
|
||||||
|
return MAGIC_TAG_PREFIX + getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a parameter model to the form model. The parameter model
|
||||||
|
* should be fully configured before adding it to the form model.
|
||||||
|
*
|
||||||
|
* @param parameter a parameter model object
|
||||||
|
* */
|
||||||
|
public final void addFormParam(ParameterModel parameter) {
|
||||||
|
Assert.exists(parameter, "Parameter");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
parameter.setDefaultOverridesNull(m_defaultOverridesNull);
|
||||||
|
m_parameterModels.add(parameter);
|
||||||
|
|
||||||
|
if( s_log.isDebugEnabled() ) {
|
||||||
|
s_log.debug( "Added parameter: " + parameter.getName() + "[" +
|
||||||
|
parameter.getClass().getName() + "]" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a parameter model to the list of parameters that should
|
||||||
|
* not be exported when the form is rendered. Useful examples
|
||||||
|
* of this are for forms that loop back on themselves such as
|
||||||
|
* search forms or a control bar. The parameter model
|
||||||
|
* should be fully configured and have been added to the form model
|
||||||
|
* before adding it to the list of items to exclude
|
||||||
|
*
|
||||||
|
* @param parameter a parameter model object
|
||||||
|
* */
|
||||||
|
public final void excludeFormParameterFromExport(ParameterModel parameter) {
|
||||||
|
Assert.exists(parameter, "Parameter");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_parametersToExclude.add(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the form model contains the specified parameter
|
||||||
|
* model.
|
||||||
|
* @param p the parameter model to check for
|
||||||
|
* @return <code>true</code> if the form model contains the specified
|
||||||
|
* parameter model; <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
public final boolean containsFormParam(ParameterModel p) {
|
||||||
|
Assert.exists(p, "Parameter");
|
||||||
|
return m_parameterModels.contains(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over the parameter models contained within
|
||||||
|
* the form model.
|
||||||
|
*
|
||||||
|
* @return an iterator over the parameter models contained within
|
||||||
|
* the form model.
|
||||||
|
* */
|
||||||
|
public final Iterator getParameters() {
|
||||||
|
return m_parameterModels.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over the parameter models that are
|
||||||
|
* contained within the form model but should not be exported
|
||||||
|
* as part of the form's state. This is important for situations
|
||||||
|
* where the form loops back on itself (e.g. a ControlBar or
|
||||||
|
* a Search form).
|
||||||
|
*/
|
||||||
|
public final Iterator getParametersToExclude() {
|
||||||
|
return m_parametersToExclude.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called as soon as the {@link FormData} has been
|
||||||
|
* initialized with the request parameters, but before any of the init,
|
||||||
|
* validation, or process listeners are run. The listener's
|
||||||
|
* <code>submitted</code> method may throw a
|
||||||
|
* <code>FormProcessException</code> to signal that any further
|
||||||
|
* processing of the form should be aborted.
|
||||||
|
*
|
||||||
|
* @param listener a <code>FormSubmissionListener</code> value
|
||||||
|
*/
|
||||||
|
public void addSubmissionListener(FormSubmissionListener listener) {
|
||||||
|
Assert.exists(listener, "Submission Listener");
|
||||||
|
m_listenerList.add(FormSubmissionListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a validation listener, implementing a custom validation
|
||||||
|
* check that applies to the form as a whole. Useful for checks
|
||||||
|
* that require examination of the values of more than one parameter.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormValidationListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addValidationListener(FormValidationListener listener) {
|
||||||
|
Assert.exists(listener, "FormValidationListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listenerList.add(FormValidationListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for form initialization events.
|
||||||
|
* <p>Initialization events occur when a form is initially
|
||||||
|
* requested by the user, but not when the form is subsequently
|
||||||
|
* submitted. They typically
|
||||||
|
* perform actions such as querying the database for existed values
|
||||||
|
* to set up an edit form or obtaining a sequence value to set up a
|
||||||
|
* create form.
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormInitListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addInitListener(FormInitListener listener) {
|
||||||
|
Assert.exists(listener, "FormInitListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listenerList.add(FormInitListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for form processing events. <p>Process events
|
||||||
|
* only occur after a form submission has been successfully
|
||||||
|
* validated. They are typically used to perform a database
|
||||||
|
* transaction or other operation based on the submitted data.
|
||||||
|
* <p>Process listeners are executed in the order in which they are
|
||||||
|
* added.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormProcessListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addProcessListener(FormProcessListener listener) {
|
||||||
|
Assert.exists(listener, "FormProcessListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listenerList.add(FormProcessListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new FormData object that is populated with default values
|
||||||
|
* (for an initial request) or values from the request (for
|
||||||
|
* a submission).
|
||||||
|
* <P>If this is a submission, validates the data and (if the
|
||||||
|
* data is valid) calls the process listeners. Returns a FormData object.
|
||||||
|
*
|
||||||
|
* @param state the PageState object holding request-specific information
|
||||||
|
* @return a FormData object.
|
||||||
|
* */
|
||||||
|
public FormData process(PageState state) throws FormProcessException {
|
||||||
|
Assert.isLocked(this);
|
||||||
|
boolean isSubmission =
|
||||||
|
state.getRequest().getParameter(getMagicTagName()) != null;
|
||||||
|
return process(state, isSubmission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new FormData object that is populated with default values
|
||||||
|
* (for an initial request) or values from the request (for a
|
||||||
|
* submission).
|
||||||
|
* <P>If this is a submission, validates the data and (if the
|
||||||
|
* data is valid) calls the process listeners. Returns a FormData object.
|
||||||
|
*
|
||||||
|
* @param state the PageState object holding request specific information
|
||||||
|
* @param isSubmission <code>true</code> if the request is a submission;
|
||||||
|
* <code>false</code> if this is the first request to the form data.
|
||||||
|
*/
|
||||||
|
public FormData process(PageState state, boolean isSubmission)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.isLocked(this);
|
||||||
|
FormData data = new FormData(this, state.getRequest(), isSubmission);
|
||||||
|
try {
|
||||||
|
process(state, data);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the work for the public process method. Uses the
|
||||||
|
* prepopulated <code>FormData</code> and runs listeners on it as
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
|
* @throws FormProcessException if an error occurs
|
||||||
|
*/
|
||||||
|
void process(final PageState state, final FormData data)
|
||||||
|
throws FormProcessException {
|
||||||
|
s_log.debug("Processing the form model");
|
||||||
|
|
||||||
|
final FormSectionEvent e = new FormSectionEvent(this, state, data);
|
||||||
|
|
||||||
|
if (data.isSubmission()) {
|
||||||
|
s_log.debug("The request is a form submission; running " +
|
||||||
|
"submission listeners");
|
||||||
|
|
||||||
|
try {
|
||||||
|
fireSubmitted(e);
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
s_log.debug("A FormProcessException was thrown while firing " +
|
||||||
|
"submit; aborting further processing");
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
s_log.debug("Validating parameters");
|
||||||
|
fireParameterValidation(e);
|
||||||
|
|
||||||
|
s_log.debug("Validating form");
|
||||||
|
fireFormValidation(e);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isValid()) {
|
||||||
|
s_log.debug("The form data is valid; running process " +
|
||||||
|
"listeners");
|
||||||
|
|
||||||
|
try {
|
||||||
|
fireFormProcess(e);
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
s_log.debug("A FormProcessException was thrown while " +
|
||||||
|
"initializing the form; storing the error", fpe);
|
||||||
|
|
||||||
|
data.addError("Initialization Aborted: " + fpe.getMessages());
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_log.debug("The form data was not valid; this form " +
|
||||||
|
"will not run its process listeners");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_log.debug("The request is not a form submission; " +
|
||||||
|
"running init listeners");
|
||||||
|
|
||||||
|
try {
|
||||||
|
fireFormInit(e);
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
s_log.debug("A FormProcessException was thrown while " +
|
||||||
|
"initializing the form; storing the error", fpe);
|
||||||
|
|
||||||
|
data.addError("Initialization Aborted: " + fpe.getMessages());
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireSubmitted(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
Assert.isLocked(this);
|
||||||
|
FormProcessException delayedException = null;
|
||||||
|
|
||||||
|
Iterator i = m_listenerList.getListenerIterator(FormSubmissionListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
try {
|
||||||
|
((FormSubmissionListener) i.next()).submitted(e);
|
||||||
|
} catch (FormProcessException ex) {
|
||||||
|
delayedException = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( delayedException != null ) {
|
||||||
|
throw delayedException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a form initialization listener.
|
||||||
|
*
|
||||||
|
* @param e a FormSectionEvent originating from the form
|
||||||
|
*/
|
||||||
|
protected void fireFormInit(FormSectionEvent e) throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
Assert.isLocked(this);
|
||||||
|
Iterator i = m_listenerList.getListenerIterator(FormInitListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
((FormInitListener) i.next()).init(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper method that validates the individual parameters by
|
||||||
|
* calling ParameterValidationListeners from the individual
|
||||||
|
* parameterModels.
|
||||||
|
*
|
||||||
|
* @param e a FormSectionEvent originating from the form
|
||||||
|
* */
|
||||||
|
protected void fireParameterValidation(FormSectionEvent e) {
|
||||||
|
FormData data = e.getFormData();
|
||||||
|
Assert.exists(data, "FormData");
|
||||||
|
Iterator parameters = getParameters();
|
||||||
|
ParameterModel parameterModel;
|
||||||
|
ParameterData parameterData;
|
||||||
|
while (parameters.hasNext()) {
|
||||||
|
parameterModel = (ParameterModel) parameters.next();
|
||||||
|
parameterData = (ParameterData) data.getParameter(parameterModel.getName());
|
||||||
|
try {
|
||||||
|
parameterData.validate();
|
||||||
|
if (!parameterData.isValid()) {
|
||||||
|
data.invalidate();
|
||||||
|
}
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
data.addError("Processing Listener Error: " + fpe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper method. Validates the form by calling
|
||||||
|
* FormValidationListeners
|
||||||
|
*
|
||||||
|
* @param e a FormSectionEvent originating from the Form
|
||||||
|
* */
|
||||||
|
private void fireFormValidation(FormSectionEvent e) {
|
||||||
|
FormData data = e.getFormData();
|
||||||
|
Assert.exists(data, "FormData");
|
||||||
|
Iterator i = m_listenerList.getListenerIterator(FormValidationListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
try {
|
||||||
|
((FormValidationListener) i.next()).validate(e);
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
data.addError(fpe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call form process listeners. <p>Form processing is performed
|
||||||
|
* <em>after</em> the form has been validated.</p>
|
||||||
|
*
|
||||||
|
* @param e a FormSectionEvent originating from the form
|
||||||
|
* */
|
||||||
|
private void fireFormProcess(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
if (!e.getFormData().isValid()) {
|
||||||
|
throw new IllegalStateException("Request data must be valid " + "prior to running processing filters.");
|
||||||
|
}
|
||||||
|
Iterator i = m_listenerList.getListenerIterator(FormProcessListener.class);
|
||||||
|
|
||||||
|
RedirectSignal redirect = null;
|
||||||
|
while (i.hasNext()) {
|
||||||
|
try {
|
||||||
|
((FormProcessListener) i.next()).process(e);
|
||||||
|
} catch( RedirectSignal signal ) {
|
||||||
|
if( s_log.isDebugEnabled() ) {
|
||||||
|
s_log.debug( "Delaying redirect to " +
|
||||||
|
signal.getDestinationURL() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( null != redirect ) {
|
||||||
|
s_log.error( "Non-deterministic redirect. Ignoring earlier occurrence.", redirect );
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect = signal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( null != redirect ) throw redirect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call form validation listeners. Listeners that encounter
|
||||||
|
* validation errors report them directly to the
|
||||||
|
* <code>FormData</code> object. <p>Form validation is performed
|
||||||
|
* <em>after</em> the initial transformation of key-value string
|
||||||
|
* pairs into Java data objects is complete.
|
||||||
|
*
|
||||||
|
* @param state the page state for this request
|
||||||
|
*
|
||||||
|
* @param data the FormData object to validate
|
||||||
|
*
|
||||||
|
* @pre data != null
|
||||||
|
* */
|
||||||
|
void validate(PageState state, FormData data) {
|
||||||
|
Assert.exists(data, "FormData");
|
||||||
|
if (!data.isTransformed()) {
|
||||||
|
throw new IllegalStateException("Request data must be transformed " + "prior to running validation filters.");
|
||||||
|
}
|
||||||
|
fireParameterValidation(new FormSectionEvent(this, state, data));
|
||||||
|
fireFormValidation(new FormSectionEvent(this, state, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge the parameterModels and Listeners from the supplied
|
||||||
|
* FormModel into the current FormModel. This method is useful when
|
||||||
|
* registering FormSections in Forms.
|
||||||
|
*
|
||||||
|
* @param m The FormModel to be merged into this FormModel
|
||||||
|
* */
|
||||||
|
void mergeModel(FormModel m) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
Assert.exists(m, "FormSection's FormModel");
|
||||||
|
m_parameterModels.addAll(m.m_parameterModels);
|
||||||
|
m_listenerList.addAll(m.m_listenerList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks this FormModel and all of its ParameterModels.
|
||||||
|
* */
|
||||||
|
public void lock() {
|
||||||
|
for (Iterator i = getParameters(); i.hasNext(); ) {
|
||||||
|
((ParameterModel) i.next()).lock();
|
||||||
|
}
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this FormModel is locked.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this FormModel is locked;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
* */
|
||||||
|
public final boolean isLocked() {
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents exceptions that occur within the processing methods
|
||||||
|
* of any of the form event listeners. Typically the code will catch specific
|
||||||
|
* exceptions such as <code>SQLException</code> and rethrow them as instances
|
||||||
|
* of this class to pass the message to the controller in a standard fashion.
|
||||||
|
*
|
||||||
|
* <p>Since this class is a subclass of <code>ServletException</code>, servlets
|
||||||
|
* that do form processing within a <code>doPost</code> or <code>doGet</code>
|
||||||
|
* methods do not need to explicitly catch instances of this class. However,
|
||||||
|
* they may wish to do so for special error reporting to the user, or to notify
|
||||||
|
* the webmaster via e-mail of the problem.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FormProcessException extends ServletException {
|
||||||
|
|
||||||
|
/** Globalized version of the exception message, intended for output in the UI */
|
||||||
|
private GlobalizedMessage m_globalizedMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor using a String as message presented to the user.
|
||||||
|
* @param message
|
||||||
|
* @deprecated Use FormProcessException(GlobalizedMessage) instead. The
|
||||||
|
* error message for the user should always be globalized so it
|
||||||
|
* can be transformed to the current users requested language.
|
||||||
|
*/
|
||||||
|
public FormProcessException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor using both types of messages which may be presented to the
|
||||||
|
* user. It's a kind of fallback just in kind we really need a non-
|
||||||
|
* globalized message. Usage is stropngly discouraged.
|
||||||
|
* @param message
|
||||||
|
* @param globalizedMessage
|
||||||
|
*/
|
||||||
|
public FormProcessException(String message,
|
||||||
|
GlobalizedMessage globalizedMessage) {
|
||||||
|
super(message);
|
||||||
|
m_globalizedMessage = globalizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor using a GlobalizedMessage as the error text presented to the
|
||||||
|
* user. Using this constructor is the strongly recommended way!
|
||||||
|
*
|
||||||
|
* @param globalizedMessage
|
||||||
|
*/
|
||||||
|
public FormProcessException(GlobalizedMessage globalizedMessage) {
|
||||||
|
super();
|
||||||
|
m_globalizedMessage = globalizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param rootCause
|
||||||
|
* @deprecated use FormProcessException(String,GlobalizedMessage,Throwable)
|
||||||
|
* instead
|
||||||
|
*/
|
||||||
|
public FormProcessException(String message,
|
||||||
|
Throwable rootCause) {
|
||||||
|
super(message, rootCause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormProcessException(String message,
|
||||||
|
GlobalizedMessage globalizedMessage,
|
||||||
|
Throwable rootCause) {
|
||||||
|
super(message, rootCause);
|
||||||
|
m_globalizedMessage = globalizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormProcessException(Throwable rootCause) {
|
||||||
|
super(rootCause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a globalized version of the exception message just in case a non-
|
||||||
|
* globalized message enabled constructor has been used.
|
||||||
|
*
|
||||||
|
* @param globalizedMessage the globalized message intended for output in UI
|
||||||
|
*/
|
||||||
|
public void setGlobalizedMessage(GlobalizedMessage globalizedMessage) {
|
||||||
|
m_globalizedMessage = globalizedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the globalized version of the exception message, intended for
|
||||||
|
* use in the UI widgets.
|
||||||
|
* The standard non-globalizatin enabled exception message is for use in
|
||||||
|
* log entries only!
|
||||||
|
*
|
||||||
|
* @return the globalized message intended for output in UI
|
||||||
|
*/
|
||||||
|
GlobalizedMessage getGlobalizedMessage() {
|
||||||
|
return m_globalizedMessage;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* In addition to printing the stack trace for this exception, also prints
|
||||||
|
* the stack trace for the root cause, if any. This is a workaround for
|
||||||
|
* those implementations of {@link ServletException} that don't implement
|
||||||
|
* <code>printStackTrace</code> correctly. If you happen to use an
|
||||||
|
* implementation that does, the stack trace for the root cause may be
|
||||||
|
* printed twice, which is not that big of a deal in the grand scheme of
|
||||||
|
* things.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace() {
|
||||||
|
super.printStackTrace();
|
||||||
|
if (getRootCause() != null) {
|
||||||
|
System.err.print("Root cause: ");
|
||||||
|
getRootCause().printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s
|
||||||
|
* @see #printStackTrace()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace(java.io.PrintStream s) {
|
||||||
|
super.printStackTrace(s);
|
||||||
|
if (getRootCause() != null) {
|
||||||
|
s.println("Root cause: ");
|
||||||
|
getRootCause().printStackTrace(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s
|
||||||
|
* @see #printStackTrace()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void printStackTrace(java.io.PrintWriter s) {
|
||||||
|
super.printStackTrace(s);
|
||||||
|
if (getRootCause() != null) {
|
||||||
|
s.println("Root cause: ");
|
||||||
|
getRootCause().printStackTrace(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the concatenation of {@link #getMessage()} and {@link
|
||||||
|
* #getRootCause()}.<code>getMessage()</code>.</p>
|
||||||
|
* @return
|
||||||
|
**/
|
||||||
|
public String getMessages() {
|
||||||
|
StringBuilder result = new StringBuilder(getMessage());
|
||||||
|
if ( getRootCause() != null ) {
|
||||||
|
result.append(" (root cause: ")
|
||||||
|
.append(getRootCause().getMessage())
|
||||||
|
.append(")");
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,777 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.EventListenerList;
|
||||||
|
import com.arsdigita.bebop.event.FormCancelListener;
|
||||||
|
import com.arsdigita.bebop.event.FormInitListener;
|
||||||
|
import com.arsdigita.bebop.event.FormProcessListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.event.FormSubmissionListener;
|
||||||
|
import com.arsdigita.bebop.event.FormValidationListener;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A standalone section of a <code>Form</code>. A <code>FormSection</code>
|
||||||
|
* contains other Bebop components, most importantly
|
||||||
|
* <code>Widgets</code> and associated listeners. It serves two purposes:
|
||||||
|
* <UL>
|
||||||
|
* <LI>Divides a form into visual sections</LI>
|
||||||
|
* <LI>Serves as a container for form fragments that can function by themselves
|
||||||
|
* and can be dropped into other forms</LI>
|
||||||
|
* </UL>
|
||||||
|
* <p>Since a <code>FormSection</code> has its own init, validation, and
|
||||||
|
* process listeners, it can do all of its processing without any intervention
|
||||||
|
* from the enclosing form.
|
||||||
|
*
|
||||||
|
* Although a <code>FormSection</code> contains all the same pieces
|
||||||
|
* that a <code>Form</code> does, it can only be used if it is added
|
||||||
|
* directly or indirectly to a <code>Form</code>. <code>FormSection</code>s
|
||||||
|
* that are not contained in a <code>Form</code> do not exhibit any useful
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* @see Form
|
||||||
|
* @see FormModel
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id: FormSection.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class FormSection extends DescriptiveComponent implements Container {
|
||||||
|
|
||||||
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int the runtime environment
|
||||||
|
* and set com.arsdigita.subsite.FormSection=DEBUG
|
||||||
|
* by uncommenting or adding the line. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(FormSection.class);
|
||||||
|
|
||||||
|
/** Underlying <code>FormModel</code> that stores
|
||||||
|
* the parameter models for all the widgets in this form section. */
|
||||||
|
protected FormModel m_formModel;
|
||||||
|
|
||||||
|
/** The container to which all children are added. A
|
||||||
|
* <code>ColumnPanel</code> by default. */
|
||||||
|
protected Container m_panel;
|
||||||
|
|
||||||
|
/** Contains all the listeners that were added with the various
|
||||||
|
* addXXXListener methods.
|
||||||
|
* We maintain our own list of listeners, so that we can re-send the
|
||||||
|
* events the FormModel generates, but with us as the source, not the
|
||||||
|
* FormModel. */
|
||||||
|
private EventListenerList m_listeners;
|
||||||
|
|
||||||
|
/** Listeners we attach to the FormModel to forward
|
||||||
|
* form model events to our listeners with the right source */
|
||||||
|
private FormSubmissionListener m_forwardSubmission;
|
||||||
|
|
||||||
|
private FormInitListener m_forwardInit;
|
||||||
|
private FormValidationListener m_forwardValidation;
|
||||||
|
private FormProcessListener m_forwardProcess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form section. Sets the implicit layout Container of
|
||||||
|
* this <code>FormSection</code> to two column <code>ColumnPanel</code>
|
||||||
|
* by calling the 1-argument constructor.
|
||||||
|
**/
|
||||||
|
public FormSection() {
|
||||||
|
this(new ColumnPanel(2, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form section. Sets the form model of this
|
||||||
|
* <code>FormSection</code> to a new, anonymous FormModel.
|
||||||
|
*
|
||||||
|
* @param panel
|
||||||
|
**/
|
||||||
|
public FormSection(Container panel) {
|
||||||
|
this(panel, new FormModel("anonymous"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new form section. Sets the implicit layout Container of
|
||||||
|
* this <code>FormSection</code> to <code>panel</code>. Sets the form
|
||||||
|
* model of this <code>FormSection</code> to <code>model</code>.
|
||||||
|
*
|
||||||
|
* @param panel the container within this form section that holds the
|
||||||
|
* components that are added to the form section with calls to the
|
||||||
|
* <code>add</code> methods
|
||||||
|
*
|
||||||
|
* @param model the form model for this form section
|
||||||
|
**/
|
||||||
|
protected FormSection(Container panel, FormModel model) {
|
||||||
|
super();
|
||||||
|
m_panel = panel;
|
||||||
|
m_formModel = model;
|
||||||
|
m_listeners = new EventListenerList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called as soon as the {@link FormData} has been
|
||||||
|
* initialized with the request parameters but before any of the init,
|
||||||
|
* validation, or process listeners are run. The listener's
|
||||||
|
* <code>submitted</code> method may throw a
|
||||||
|
* <code>FormProcessException</code> to signal that any further
|
||||||
|
* processing of the form should be aborted.
|
||||||
|
*
|
||||||
|
* @param listener a submission listener to run every time the form is
|
||||||
|
* submitted
|
||||||
|
* @see FormModel#addSubmissionListener
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void addSubmissionListener(FormSubmissionListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Adding submission listener " + listener + " to " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Submission Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
forwardSubmission();
|
||||||
|
m_listeners.add(FormSubmissionListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified submission listener from the
|
||||||
|
* list of submission listeners (if it had previously been added).
|
||||||
|
*
|
||||||
|
* @param listener the submission listener to remove
|
||||||
|
*/
|
||||||
|
public void removeSubmissionListener(FormSubmissionListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Removing submission listener " + listener + " from "
|
||||||
|
+ this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Submission Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(FormSubmissionListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>submitted</code> method on all registered submission
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @param e the event to pass to the listeners
|
||||||
|
* @throws FormProcessException if one of the listeners throws such an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
protected void fireSubmitted(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
FormProcessException delayedException = null;
|
||||||
|
|
||||||
|
Iterator i = m_listeners.getListenerIterator(
|
||||||
|
FormSubmissionListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
final FormSubmissionListener listener = (FormSubmissionListener) i.
|
||||||
|
next();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Firing submission listener " + listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
listener.submitted(e);
|
||||||
|
} catch (FormProcessException ex) {
|
||||||
|
s_log.debug(ex);
|
||||||
|
delayedException = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (delayedException != null) {
|
||||||
|
throw delayedException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void forwardSubmission() {
|
||||||
|
if (m_forwardSubmission == null) {
|
||||||
|
m_forwardSubmission = createSubmissionListener();
|
||||||
|
getModel().addSubmissionListener(m_forwardSubmission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the submission listener that forwards submission events to this
|
||||||
|
* form section.
|
||||||
|
*
|
||||||
|
* @return a submission listener that forwards submission events to this
|
||||||
|
* form section.
|
||||||
|
*/
|
||||||
|
protected FormSubmissionListener createSubmissionListener() {
|
||||||
|
return new FormSubmissionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void submitted(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
fireSubmitted(new FormSectionEvent(FormSection.this,
|
||||||
|
e.getPageState(),
|
||||||
|
e.getFormData()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for form initialization events. Initialization
|
||||||
|
* events occur when a form is initially requested by the user, but
|
||||||
|
* not when the form is subsequently submitted. They typically
|
||||||
|
* perform actions such as querying the database for existing values
|
||||||
|
* to set up an edit form, or obtaining a sequence value to set up a
|
||||||
|
* create form.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormInitListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addInitListener(FormInitListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Adding init listener " + listener + " to " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "FormInitListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
forwardInit();
|
||||||
|
m_listeners.add(FormInitListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified init listener from the
|
||||||
|
* list of init listeners (if it had previously been added).
|
||||||
|
*
|
||||||
|
* @param listener the init listener to remove
|
||||||
|
*/
|
||||||
|
public void removeInitListener(FormInitListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Removing init listener " + listener + " from " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Init Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(FormInitListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>init</code> method on all registered init
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @param e the event to pass to the listeners
|
||||||
|
* @throws FormProcessException if one of the listeners throws such an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
protected void fireInit(FormSectionEvent e) throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
Assert.isLocked(this);
|
||||||
|
Iterator i = m_listeners.getListenerIterator(FormInitListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
final FormInitListener listener = (FormInitListener) i.next();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Firing init listener " + listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.init(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void forwardInit() {
|
||||||
|
if (m_forwardInit == null) {
|
||||||
|
m_forwardInit = createInitListener();
|
||||||
|
getModel().addInitListener(m_forwardInit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the init listener that forwards init events to this form
|
||||||
|
* section.
|
||||||
|
*
|
||||||
|
* @return an init listener that forwards init events to this
|
||||||
|
* form section.
|
||||||
|
*/
|
||||||
|
protected FormInitListener createInitListener() {
|
||||||
|
return new FormInitListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
fireInit(new FormSectionEvent(FormSection.this,
|
||||||
|
e.getPageState(),
|
||||||
|
e.getFormData()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the cancel listener that forwards cancel events to this form
|
||||||
|
* section
|
||||||
|
*
|
||||||
|
* @return an cancel listener
|
||||||
|
*/
|
||||||
|
protected FormCancelListener createCancelListener() {
|
||||||
|
return new FormCancelListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(FormSectionEvent e) throws FormProcessException {
|
||||||
|
fireCancel(new FormSectionEvent(FormSection.this,
|
||||||
|
e.getPageState(),
|
||||||
|
e.getFormData()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a validation listener, implementing a custom validation
|
||||||
|
* check that applies to the form as a whole. Useful for checks
|
||||||
|
* that require examination of the values of more than one parameter.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormValidationListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addValidationListener(FormValidationListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Adding validation listener " + listener + " to " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "FormValidationListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
forwardValidation();
|
||||||
|
m_listeners.add(FormValidationListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified validation listener from the
|
||||||
|
* list of validation listeners (if it had previously been added).
|
||||||
|
*
|
||||||
|
* @param listener a validation listener
|
||||||
|
*/
|
||||||
|
public void removeValidationListener(FormValidationListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Removing validation listener " + listener + " from "
|
||||||
|
+ this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Validation Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(FormValidationListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>validate</code> method on all registered validation
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @param e the event to pass to the listeners
|
||||||
|
*/
|
||||||
|
protected void fireValidate(FormSectionEvent e) {
|
||||||
|
FormData data = e.getFormData();
|
||||||
|
Assert.exists(data, "FormData");
|
||||||
|
Iterator i = m_listeners.getListenerIterator(
|
||||||
|
FormValidationListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
try {
|
||||||
|
final FormValidationListener listener =
|
||||||
|
(FormValidationListener) i.next();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Firing validation listener " + listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.validate(e);
|
||||||
|
} catch (FormProcessException fpe) {
|
||||||
|
s_log.debug(fpe);
|
||||||
|
data.addError(fpe.getGlobalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forwardValidation() {
|
||||||
|
if (m_forwardValidation == null) {
|
||||||
|
m_forwardValidation = createValidationListener();
|
||||||
|
getModel().addValidationListener(m_forwardValidation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the validation listener that forwards validation events to this
|
||||||
|
* form section.
|
||||||
|
*
|
||||||
|
* @return a validation listener that forwards validation events to this
|
||||||
|
* form section.
|
||||||
|
*/
|
||||||
|
protected FormValidationListener createValidationListener() {
|
||||||
|
return new FormValidationListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(FormSectionEvent e) {
|
||||||
|
fireValidate(new FormSectionEvent(FormSection.this,
|
||||||
|
e.getPageState(),
|
||||||
|
e.getFormData()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for form processing events. <p>Process events
|
||||||
|
* only occur after a form submission has been successfully
|
||||||
|
* validated. They are typically used to perform a database
|
||||||
|
* transaction or other operation based on the submitted data.
|
||||||
|
* <p>Process listeners are executed in the order in which
|
||||||
|
* they are added.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormProcessListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addProcessListener(final FormProcessListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Adding process listener " + listener + " to " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "FormProcessListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
|
||||||
|
forwardProcess();
|
||||||
|
m_listeners.add(FormProcessListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified process listener from the
|
||||||
|
* list of process listeners (if it had previously been added).
|
||||||
|
*
|
||||||
|
* @param listener the process listener to remove
|
||||||
|
*/
|
||||||
|
public void removeProcessListener(FormProcessListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Removing process listener " + listener + " from "
|
||||||
|
+ this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Process Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
|
||||||
|
m_listeners.remove(FormProcessListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void forwardProcess() {
|
||||||
|
if (m_forwardProcess == null) {
|
||||||
|
m_forwardProcess = createProcessListener();
|
||||||
|
getModel().addProcessListener(m_forwardProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FormProcessListener createProcessListener() {
|
||||||
|
return new FormProcessListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
fireProcess(new FormSectionEvent(FormSection.this,
|
||||||
|
e.getPageState(),
|
||||||
|
e.getFormData()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>process</code> method on all registered process
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @param e the event to pass to the listeners
|
||||||
|
* @throws FormProcessException if one of the listeners throws such an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
protected void fireProcess(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
Iterator i = m_listeners.getListenerIterator(FormProcessListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
final FormProcessListener listener = (FormProcessListener) i.next();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Firing process listener " + listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.process(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since a form section cannot be processed, always throws an error.
|
||||||
|
* (Processing of form sections is done by the form in which the
|
||||||
|
* section is contained.)
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws javax.servlet.ServletException because processing a form section
|
||||||
|
* is not meaningful.
|
||||||
|
*/
|
||||||
|
public FormData process(PageState data)
|
||||||
|
throws javax.servlet.ServletException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for form cancellation events. Cancellation
|
||||||
|
* listeners are typically used to clean-up page state and
|
||||||
|
* potentially intermediate changes to the database.
|
||||||
|
*
|
||||||
|
* @param listener an instance of a class that implements the
|
||||||
|
* <code>FormCancelListener</code> interface
|
||||||
|
* */
|
||||||
|
public void addCancelListener(FormCancelListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Adding cancel listener " + listener + " to " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "FormCancelListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.add(FormCancelListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified cancellation listener from the
|
||||||
|
* list of cancellation listeners (if it had previously been added).
|
||||||
|
*
|
||||||
|
* @param listener the cancellation listener to remove
|
||||||
|
*/
|
||||||
|
public void removeCancelListener(FormCancelListener listener) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Removing cancel listener " + listener + " from " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(listener, "Cancel Listener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(FormCancelListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>cancel</code> method on all registered cancellation
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @param e the event to pass to the listeners
|
||||||
|
* @throws FormProcessException if one of the listeners throws such an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
protected void fireCancel(FormSectionEvent e)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.exists(e.getFormData(), "FormData");
|
||||||
|
Iterator i = m_listeners.getListenerIterator(FormCancelListener.class);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
final FormCancelListener listener = (FormCancelListener) i.next();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Firing cancel listener " + listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.cancel(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the children this FormSection, collecting parameter models
|
||||||
|
* and listeners into the supplied FormModel. Sets implicit pointers
|
||||||
|
* of widgets in this FormSection to the supplied Form.
|
||||||
|
*
|
||||||
|
* @param f pointer to the form that is set inside Widgets within this
|
||||||
|
* FormSection
|
||||||
|
* @param m the FormModel in which to merge ParameterModels and
|
||||||
|
* Listeners
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public void register(Form f, FormModel m) {
|
||||||
|
m.mergeModel(getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor method for this form's FormModel.
|
||||||
|
*
|
||||||
|
* @return FormModel The model of this form.
|
||||||
|
* */
|
||||||
|
protected final FormModel getModel() {
|
||||||
|
return m_formModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks this FormSection, its FormModel, and the implicit Container.
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public void lock() {
|
||||||
|
m_formModel.lock();
|
||||||
|
m_panel.lock();
|
||||||
|
super.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void respond(PageState state) throws javax.servlet.ServletException {
|
||||||
|
//call listeners here.
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the implicit Container of this FormSection.
|
||||||
|
*
|
||||||
|
* This must not be final, because MetaFrom needs to override it.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Container getPanel() {
|
||||||
|
return m_panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over the children of this component. If the
|
||||||
|
* component has no children, returns an empty iterator (not
|
||||||
|
* <code>null</code> !).
|
||||||
|
*
|
||||||
|
* @post return != null
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public Iterator children() {
|
||||||
|
return m_panel.children();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an XML subtree for this component under the specified
|
||||||
|
* <code>parent</code>. Uses the request values stored in
|
||||||
|
* <code>state</code>.</p>
|
||||||
|
*
|
||||||
|
* <p> This method generates DOM to be used with the XSLT template
|
||||||
|
* to produce the appropriate output.</p>
|
||||||
|
*
|
||||||
|
* @param pageState the state of the current page
|
||||||
|
* @param parent the node that will be used to write to
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState pageState, Element parent) {
|
||||||
|
if (isVisible(pageState)) {
|
||||||
|
m_panel.generateXML(pageState, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container methods
|
||||||
|
/**
|
||||||
|
* Adds a component to this container.
|
||||||
|
*
|
||||||
|
* @param pc the component to add to this container
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public void add(Component pc) {
|
||||||
|
m_panel.add(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component with the specified layout constraints to this
|
||||||
|
* container. Layout constraints are defined in each layout container as
|
||||||
|
* static ints. Use a bitwise OR to specify multiple constraints.
|
||||||
|
*
|
||||||
|
* @param pc the component to add to this container
|
||||||
|
* @param constraints layout constraints (a bitwise OR of static ints in
|
||||||
|
* the particular layout)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void add(Component pc, int constraints) {
|
||||||
|
m_panel.add(pc, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if this list contains the
|
||||||
|
* specified element. More
|
||||||
|
* formally, returns true if and only if this list contains at least
|
||||||
|
* one element e such that (o==null ? e==null : o.equals(e)).
|
||||||
|
*
|
||||||
|
* This method returns <code>true</code> only if the component has
|
||||||
|
* been directly
|
||||||
|
* added to this container. If this container contains another
|
||||||
|
* container that contains this component, this method returns
|
||||||
|
* <code>false</code>.
|
||||||
|
*
|
||||||
|
* @param o element whose presence in this container is to be tested
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this Container contains the
|
||||||
|
* specified component directly; <code>false</code> otherwise.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return m_panel.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the
|
||||||
|
* Component at the specified position. Each call to add()
|
||||||
|
* increments the index. This method should be used in conjunction
|
||||||
|
* with indexOf
|
||||||
|
*
|
||||||
|
* @param index The index of the item to be retrieved from this
|
||||||
|
* Container. Since the user has no control over the index of added
|
||||||
|
* components (other than counting each call to add), this method
|
||||||
|
* should be used in conjunction with indexOf.
|
||||||
|
*
|
||||||
|
* @return the component at the specified position in this container
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public Component get(int index) {
|
||||||
|
return (Component) m_panel.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param pc component to search for
|
||||||
|
*
|
||||||
|
* @return the index in this list of the first occurrence of
|
||||||
|
* the specified element, or -1 if this list does not contain this
|
||||||
|
* element.
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public int indexOf(Component pc) {
|
||||||
|
return m_panel.indexOf(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the container contains any components.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this container contains no components
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return m_panel.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements in this container. This does not
|
||||||
|
* recursively count the components indirectly contained in this container.
|
||||||
|
*
|
||||||
|
* @return the number of components directly in this container.
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return m_panel.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.FormInitListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.form.Widget;
|
||||||
|
import com.arsdigita.bebop.parameters.BooleanParameter;
|
||||||
|
import com.arsdigita.bebop.util.Traversal;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FormStep class modifies the behavior of FormSection with respect to
|
||||||
|
* listener firing. Instead of firing init listeners the first time the
|
||||||
|
* enclosing form is displayed on the page, the FormStep class fires init
|
||||||
|
* listeners the first time the FormStep itself is displayed on the page. The
|
||||||
|
* process, validate, and submission listeners are then fired on every
|
||||||
|
* submission following the one in which the init listeners were fired. This
|
||||||
|
* behavior is useful when used in conjunction with {@link MultiStepForm} or
|
||||||
|
* its subclasses to provide initialization in later steps of a multi step
|
||||||
|
* form that depends on the values entered in earlier steps.
|
||||||
|
*
|
||||||
|
* updated chris.gilbert@westsussex.gov.uk - support for session based wizards
|
||||||
|
* (which enable use of actionlinks in wizard)
|
||||||
|
|
||||||
|
* @see Wizard
|
||||||
|
* @see MultiStepForm
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:rhs@mit.edu">rhs@mit.edu</a>
|
||||||
|
* @version $Id: FormStep.java 1414 2006-12-07 14:24:10Z chrisgilbert23 $
|
||||||
|
**/
|
||||||
|
|
||||||
|
public class FormStep extends FormSection {
|
||||||
|
private final static Logger s_log = Logger.getLogger(FormStep.class);
|
||||||
|
|
||||||
|
private Form m_form = null;
|
||||||
|
|
||||||
|
// cg - changed to using a parameter that is stored in pagestate so that if there are links
|
||||||
|
// within the form then the init status of the steps is not lost
|
||||||
|
// private Hidden m_initialized;
|
||||||
|
|
||||||
|
private BooleanParameter m_initialized;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new FormStep with the given name. The name must uniquely
|
||||||
|
* identify this FormStep within it's enclosing Form.
|
||||||
|
*
|
||||||
|
* @param name A name that uniquely identifies this FormStep within it's
|
||||||
|
* enclosing Form.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public FormStep(String name) {
|
||||||
|
addInitialized(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new FormStep with the given name. The name must uniquely
|
||||||
|
* identify this FormStep within it's enclosing Form.
|
||||||
|
*
|
||||||
|
* @param name A name that uniquely identifies this FormStep within it's
|
||||||
|
* enclosing Form.
|
||||||
|
* @param panel The container used to back this FormStep.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public FormStep(String name, Container panel) {
|
||||||
|
super(panel);
|
||||||
|
addInitialized(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FormStep(String name, Container panel, FormModel model) {
|
||||||
|
super(panel, model);
|
||||||
|
addInitialized(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Page p) {
|
||||||
|
super.register(p);
|
||||||
|
p.addComponentStateParam(this, m_initialized);
|
||||||
|
Traversal trav = new Traversal () {
|
||||||
|
protected void act(Component c) {
|
||||||
|
if (c instanceof Widget) {
|
||||||
|
((Widget) c).setValidateInvisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
trav.preorder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Form form, FormModel model) {
|
||||||
|
super.register(form, model);
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInitialized(String name) {
|
||||||
|
// m_initialized = new Hidden(new BooleanParameter(name));
|
||||||
|
// add(m_initialized);
|
||||||
|
m_initialized = new BooleanParameter(name);
|
||||||
|
m_initialized.setDefaultValue(Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized(PageState ps) {
|
||||||
|
// Object init = m_initialized.getValue(ps);
|
||||||
|
Boolean init = (Boolean)ps.getValue(m_initialized);
|
||||||
|
if (init == null) {
|
||||||
|
s_log.debug("init for step " + m_initialized.getName() + " is null. returning true");
|
||||||
|
// happens if step state is stored in session -
|
||||||
|
// form containing this step clears session
|
||||||
|
// info when processed, but fireProcess invoked
|
||||||
|
// on this step AFTER form is processed. At that point,
|
||||||
|
// the step has been initialised because we are on the
|
||||||
|
// final process at the end of the steps
|
||||||
|
//
|
||||||
|
init = Boolean.TRUE;
|
||||||
|
}
|
||||||
|
return init.booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInitialized(PageState ps) {
|
||||||
|
//m_initialized.setValue(ps, Boolean.TRUE);
|
||||||
|
ps.setValue(m_initialized, Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn off forwarding of init events.
|
||||||
|
protected FormInitListener createInitListener() {
|
||||||
|
return new FormInitListener() {
|
||||||
|
public void init(FormSectionEvent evt) { }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireSubmitted(FormSectionEvent evt)
|
||||||
|
throws FormProcessException {
|
||||||
|
if (isInitialized(evt.getPageState())) {
|
||||||
|
super.fireSubmitted(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireValidate(FormSectionEvent evt) {
|
||||||
|
if (isInitialized(evt.getPageState())) {
|
||||||
|
super.fireValidate(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireProcess(FormSectionEvent evt)
|
||||||
|
throws FormProcessException {
|
||||||
|
s_log.debug("fireprocess invoked on Formstep " + m_initialized.getName());
|
||||||
|
if (isInitialized(evt.getPageState())) {
|
||||||
|
super.fireProcess(evt);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateXML(PageState ps, Element parent) {
|
||||||
|
if (!isInitialized(ps)) {
|
||||||
|
FormData fd = m_form.getFormData(ps);
|
||||||
|
try {
|
||||||
|
fireInit(new FormSectionEvent(this, ps, fd));
|
||||||
|
setInitialized(ps);
|
||||||
|
} catch (FormProcessException ex) {
|
||||||
|
s_log.debug("initialization aborted", ex);
|
||||||
|
fd.addError("Initialization Aborted: " + ex.getMessages());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.generateXML(ps, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,344 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.bebop.util.PanelConstraints;
|
||||||
|
import com.arsdigita.bebop.form.Hidden;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A container that prints its components in a table. Each child is
|
||||||
|
* printed in its own table cell. The number of columns can be
|
||||||
|
* specified in the constructor. The components are put into the table
|
||||||
|
* in the order in which they were added to the <code>GridPanel</code>
|
||||||
|
* by filling the table one row
|
||||||
|
* at a time (filling each row from left to right), from the top of the table
|
||||||
|
* to the bottom.</p>
|
||||||
|
*
|
||||||
|
* <p>The position of the component within the cell can be influenced
|
||||||
|
* with the following constraints.</p>
|
||||||
|
*
|
||||||
|
* <TABLE border=0>
|
||||||
|
* <tr>
|
||||||
|
* <TD nowrap valign="top">Horizontal alignment</TD>
|
||||||
|
* <Td valign="top">Use <code>LEFT</code>, <code>CENTER</code>, or
|
||||||
|
* <code>RIGHT</code>.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Vertical alignment</td>
|
||||||
|
* <td valign="top">Use <code>TOP</code>, <code>MIDDLE</code>, or
|
||||||
|
* <code>BOTTOM</code>.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Full width</td>
|
||||||
|
* <td valign="top">Use <code>FULL_WIDTH</code> to instruct the panel to put
|
||||||
|
* the component in a row by itself, spanning the full width of the
|
||||||
|
* table.</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td nowrap valign="top">Inserting children</td>
|
||||||
|
* <td valign="top">Use <code>INSERT</code> to instruct the panel to
|
||||||
|
* insert the corresponding component, assuming that it will also be
|
||||||
|
* laid out by a <code>ColumnPanel</code> with the same number of
|
||||||
|
* columns.</td></tr>
|
||||||
|
* </TABLE>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
* <p>Constraints can be combined by
|
||||||
|
* ORing them together. For example, to print a component in a row of its
|
||||||
|
* own, left-aligned, at the bottom of its cell, use the constraint
|
||||||
|
* <code>FULL_WIDTH | LEFT | BOTTOM</code>.</p>
|
||||||
|
*
|
||||||
|
* <p>Using the <code>INSERT</code> constraint fuses the current
|
||||||
|
* <code>GridPanel</code> with the panel of the child to which the
|
||||||
|
* constraint is applied. For example, consider a {@link Form}, that
|
||||||
|
* is to have a 2-column format with labels in the left column
|
||||||
|
* and widgets in the right column. If a {@link FormSection} is added to
|
||||||
|
* the form, it should be included seamlessly into the parent
|
||||||
|
* form. To do this, set the <code>INSERT</code>
|
||||||
|
* constraint when the {@link FormSection} is added to the {@link
|
||||||
|
* Form}'s <code>GridPanel</code>. At the same time, tell the
|
||||||
|
* <code>GridPanel</code> used to lay out the {@link FormSection}
|
||||||
|
* that it is is to be inserted into another panel.
|
||||||
|
* The following
|
||||||
|
* pseudo-code illustrates the example. (It assumes that Form and
|
||||||
|
* FormSection are decorators of the GridPanel.)</p>
|
||||||
|
*
|
||||||
|
* <blockquote><pre>
|
||||||
|
* Form form = new Form(new GridPanel(2));
|
||||||
|
* FormSection sec = new FormSection(new GridPanel(2, true));
|
||||||
|
* // "true" in the above constructor tells the GridPanel it is inserted.
|
||||||
|
*
|
||||||
|
* sec.add(new Label("Basic Item Metadata"), GridPanel.FULL_WIDTH);
|
||||||
|
* sec.add(new Label("Title:"), GridPanel.RIGHT);
|
||||||
|
* sec.add(new Text("title"));
|
||||||
|
*
|
||||||
|
* form.add(sec, GridPanel.INSERT);
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* @see BoxPanel
|
||||||
|
* @see SplitPanel
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @author Justin Ross
|
||||||
|
* @version $Id: GridPanel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class GridPanel extends SimpleContainer
|
||||||
|
implements BebopConstants, PanelConstraints {
|
||||||
|
|
||||||
|
private static final ChildConstraint DEFAULT_CONSTRAINT
|
||||||
|
= new ChildConstraint();
|
||||||
|
|
||||||
|
private int m_numColumns;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Explicitly registered constraints for child components. Maps
|
||||||
|
* <code>Components</code>s to <code>Constraints</code>
|
||||||
|
*/
|
||||||
|
private Map m_childConstraintMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is this panel inserted in another one? If so, do not produce
|
||||||
|
* <table> tags.
|
||||||
|
*/
|
||||||
|
private boolean m_isInserted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table panel with the specified number of columns.
|
||||||
|
*
|
||||||
|
* @param numColumns the number of columns in the panel
|
||||||
|
*/
|
||||||
|
public GridPanel(int numColumns) {
|
||||||
|
this(numColumns, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table panel with the specified number of columns and
|
||||||
|
* indicates whether the panel is inserted.
|
||||||
|
*
|
||||||
|
* @param numColumns the number of columns in the panel
|
||||||
|
* @param isInserted <code>true</code> if this panel is to be
|
||||||
|
* printed as a direct child of a <code>GridPanel</code>
|
||||||
|
* with the same number of columns
|
||||||
|
* @see #setInserted
|
||||||
|
*/
|
||||||
|
public GridPanel(int numColumns, boolean isInserted) {
|
||||||
|
m_numColumns = numColumns;
|
||||||
|
setInserted(isInserted);
|
||||||
|
m_childConstraintMap = new HashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component, specifying constraints.
|
||||||
|
* @param component the component to add
|
||||||
|
* @param constraints the constraints for the component
|
||||||
|
*/
|
||||||
|
public void add(Component component, int constraints) {
|
||||||
|
super.add(component);
|
||||||
|
|
||||||
|
m_childConstraintMap.put(component, new ChildConstraint(constraints));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this panel will be printed inside a
|
||||||
|
* <code>GridPanel</code> with the same number of columns. If
|
||||||
|
* <code>inserted</code> is <code>true</code>, no <table> tags will be
|
||||||
|
* produced to enclose the child components.
|
||||||
|
* @param <code>true</code> if this panel is to be printed
|
||||||
|
* inside a GridPanel with the same number of columns
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setInserted(boolean isInserted) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_isInserted = isInserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether this panel is to be inserted into another panel.
|
||||||
|
* @return <code>true</code> if this panel is to be inserted into another panel;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
* @see #setInserted
|
||||||
|
*/
|
||||||
|
public final boolean isInserted() {
|
||||||
|
return m_isInserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds child components as a subtree under table-style nodes. If any of the
|
||||||
|
* direct children are hidden form widgets, they are added directly to
|
||||||
|
* <code>parent</code> rather than included in any of the
|
||||||
|
* <code>cell</code> elements of the panel.
|
||||||
|
*
|
||||||
|
* <p>Generates a DOM fragment:
|
||||||
|
* <p><code><pre>
|
||||||
|
* <bebop:gridPanel>
|
||||||
|
* <bebop:panelRow>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* ...
|
||||||
|
* </bebop:panelRow>
|
||||||
|
* <bebop:panelRow>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* <bebop:cell> ... cell contents </bebop:cell>
|
||||||
|
* ...
|
||||||
|
* </bebop:panelRow>
|
||||||
|
* </bebop:gridPanel></pre></code>
|
||||||
|
*
|
||||||
|
* @param pageState
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState pageState, Element parent) {
|
||||||
|
if (isVisible(pageState)) {
|
||||||
|
if (isInserted()) {
|
||||||
|
generateChildren(pageState, parent);
|
||||||
|
} else {
|
||||||
|
Element panel = parent.newChildElement(BEBOP_GRIDPANEL, BEBOP_XML_NS);
|
||||||
|
exportAttributes(panel);
|
||||||
|
generateChildren(pageState, panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lay out the child components using constraints registered for them,
|
||||||
|
* generating a DOM tree and extending another.
|
||||||
|
*/
|
||||||
|
private void generateChildren(PageState pageState, Element parent) {
|
||||||
|
int positionInRow = 0;
|
||||||
|
boolean newRowRequested = true; // First time through we want a new row.
|
||||||
|
Element row = null;
|
||||||
|
Element cell = null;
|
||||||
|
ChildConstraint constraint = null;
|
||||||
|
|
||||||
|
Iterator iter = children();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Component child = (Component)iter.next();
|
||||||
|
|
||||||
|
if (child.isVisible(pageState)) {
|
||||||
|
if (child instanceof Hidden) {
|
||||||
|
child.generateXML(pageState, parent);
|
||||||
|
} else {
|
||||||
|
constraint = getChildConstraint(child);
|
||||||
|
|
||||||
|
if (constraint.m_isInsert) {
|
||||||
|
child.generateXML(pageState, parent);
|
||||||
|
|
||||||
|
newRowRequested = true;
|
||||||
|
} else {
|
||||||
|
if (positionInRow >= m_numColumns
|
||||||
|
|| constraint.m_isFullWidth
|
||||||
|
|| newRowRequested) {
|
||||||
|
positionInRow = 0;
|
||||||
|
|
||||||
|
row = parent.newChildElement(BEBOP_PANELROW, BEBOP_XML_NS);
|
||||||
|
|
||||||
|
if (constraint.m_isFullWidth) {
|
||||||
|
// If the column was full width, we
|
||||||
|
// want a new row in the next iteration.
|
||||||
|
newRowRequested = true;
|
||||||
|
} else if (newRowRequested) {
|
||||||
|
// Reset to off.
|
||||||
|
newRowRequested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cell = row.newChildElement(BEBOP_CELL, BEBOP_XML_NS);
|
||||||
|
|
||||||
|
child.generateXML(pageState, cell);
|
||||||
|
|
||||||
|
constraint.exportCellAttributes(cell, m_numColumns);
|
||||||
|
|
||||||
|
positionInRow++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper stuff
|
||||||
|
*/
|
||||||
|
|
||||||
|
private ChildConstraint getChildConstraint(Component component) {
|
||||||
|
ChildConstraint constraint =
|
||||||
|
(ChildConstraint)m_childConstraintMap.get(component);
|
||||||
|
|
||||||
|
if (constraint == null) {
|
||||||
|
constraint = DEFAULT_CONSTRAINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ChildConstraint {
|
||||||
|
public boolean m_isFullWidth;
|
||||||
|
public boolean m_isInsert;
|
||||||
|
public String m_horizontalAlignment;
|
||||||
|
public String m_verticalAlignment;
|
||||||
|
|
||||||
|
public ChildConstraint() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildConstraint(int constraints) {
|
||||||
|
if ((constraints & LEFT) != 0) {
|
||||||
|
m_horizontalAlignment = "left";
|
||||||
|
} else if ((constraints & CENTER) != 0) {
|
||||||
|
m_horizontalAlignment = "center";
|
||||||
|
} else if ((constraints & RIGHT) != 0) {
|
||||||
|
m_horizontalAlignment = "right";
|
||||||
|
} else {
|
||||||
|
m_horizontalAlignment = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((constraints & TOP) != 0) {
|
||||||
|
m_verticalAlignment = "top";
|
||||||
|
} else if ((constraints & MIDDLE) != 0) {
|
||||||
|
m_verticalAlignment = "middle";
|
||||||
|
} else if ((constraints & BOTTOM) != 0) {
|
||||||
|
m_verticalAlignment = "bottom";
|
||||||
|
} else {
|
||||||
|
m_verticalAlignment = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isFullWidth = (constraints & FULL_WIDTH) != 0;
|
||||||
|
|
||||||
|
m_isInsert = (constraints & INSERT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exportCellAttributes(Element cell, int numColumns) {
|
||||||
|
if (m_horizontalAlignment != null) {
|
||||||
|
cell.addAttribute("align", m_horizontalAlignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_verticalAlignment != null) {
|
||||||
|
cell.addAttribute("valign", m_verticalAlignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isFullWidth) {
|
||||||
|
cell.addAttribute("colspan", Integer.toString(numColumns));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,447 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text label displayed to the user for information about and identification
|
||||||
|
* of certain parts of the screen. Therefore the label has to use a
|
||||||
|
* GlobalizedMessage for the information presented.
|
||||||
|
*
|
||||||
|
* A Label is meant to provide semantically relevant informatin and may not be
|
||||||
|
* used for fixed arbitrary Text. Use Embedded instead.
|
||||||
|
*
|
||||||
|
* (Previous usage: can be used to generate either some static, fixed
|
||||||
|
* text or a new text string for every request.)
|
||||||
|
*
|
||||||
|
* To modify the information with an already locked label use the {@link
|
||||||
|
* #setLabel(String,PageState)} method which can adjust for each request.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: Label.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class Label extends DescriptiveComponent implements Cloneable {
|
||||||
|
|
||||||
|
public static final String BOLD = "b";
|
||||||
|
public static final String ITALIC = "i";
|
||||||
|
|
||||||
|
// the default label
|
||||||
|
private GlobalizedMessage m_label;
|
||||||
|
// a requestlocal set of labels (to avoid printlisteners)
|
||||||
|
private final RequestLocal m_requestLabel = new RequestLocal();
|
||||||
|
private String m_fontWeight;
|
||||||
|
|
||||||
|
/** The setting for output escaping affects how markup in the
|
||||||
|
* <code>content</code> is handled.
|
||||||
|
* <UL><LI>If output escaping is in effect (true), <b>example</b>
|
||||||
|
* will appear literally.</LI>
|
||||||
|
* <LI>If output escaping is disabled, <b>example</b> appears as the
|
||||||
|
* String "example" in bold (i.e. retaining the markup.</LI></UL>
|
||||||
|
* Default is false. */
|
||||||
|
private boolean m_escaping = false; // default for a primitive anyway
|
||||||
|
private PrintListener m_printListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor creates a new <code>Label</code> with empty text.
|
||||||
|
*/
|
||||||
|
public Label() {
|
||||||
|
// A kind of fallback (or a hack) here. Parameter label is taken as
|
||||||
|
// a key for some (unknown) Resource bundle. Because GlobalizedMessage
|
||||||
|
// will not find a corrresponding message it will display the key
|
||||||
|
// itself, 'faking' a globalized message.
|
||||||
|
m_label = new GlobalizedMessage(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>Label</code> with the specified (fixed) text.
|
||||||
|
*
|
||||||
|
* @param label the text to display
|
||||||
|
* @deprecated refactor to use Label(GlobalizedMessage label) instead
|
||||||
|
*/
|
||||||
|
public Label(String label) {
|
||||||
|
this(label, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>Label</code> with the specified text and
|
||||||
|
* output escaping turned on if <code>escaping</code> is <code>true</code>.
|
||||||
|
*
|
||||||
|
* The setting for output escaping affects how markup in the
|
||||||
|
* <code>label</code> is handled. For example:
|
||||||
|
* <UL><LI>If output escaping is in effect, <b>text</b> will appear
|
||||||
|
* literally.</LI>
|
||||||
|
* <LI>If output escaping is disabled, <b>text</b> appears as the
|
||||||
|
* word "text" in bold.</LI></UL>
|
||||||
|
*
|
||||||
|
* @param label the text to display
|
||||||
|
* @param escaping <code>true</code> if output escaping will be in effect;
|
||||||
|
* <code>false</code> if output escaping will be disabled
|
||||||
|
*
|
||||||
|
* @deprecated refactor to Label(GlobalizedMessage label, boolean escaping)
|
||||||
|
* instead
|
||||||
|
*/
|
||||||
|
public Label(String label, boolean escaping) {
|
||||||
|
setLabel(label);
|
||||||
|
setOutputEscaping(escaping);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Creates a new label with the specified text. </p>
|
||||||
|
*
|
||||||
|
* @param label the text to display
|
||||||
|
*/
|
||||||
|
public Label(GlobalizedMessage label) {
|
||||||
|
this(label, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new label with the specified text as GlobalizedMessage
|
||||||
|
* and fontweight.
|
||||||
|
*
|
||||||
|
* @param label The text to display as GlobalizedMessage
|
||||||
|
* @param fontWeight The fontWeight e.g., Label.BOLD. Whether it has any
|
||||||
|
* effect depends on the theme! Take it just as a hint.
|
||||||
|
*/
|
||||||
|
public Label(GlobalizedMessage label, String fontWeight) {
|
||||||
|
this(label, true);
|
||||||
|
m_fontWeight = fontWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> Creates a new label with the specified text as GlobalizedMessage
|
||||||
|
* and output escaping turned on if <code>escaping</code> is
|
||||||
|
* <code>true</code>. </p>
|
||||||
|
*
|
||||||
|
* @param label the text to display as GlobalizedMessage
|
||||||
|
* @param escaping Whether or not to perform output escaping
|
||||||
|
*/
|
||||||
|
public Label(GlobalizedMessage label, boolean escaping) {
|
||||||
|
setLabel(label);
|
||||||
|
setOutputEscaping(escaping);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>Label</code> that uses the print listener to
|
||||||
|
* generate output.
|
||||||
|
*
|
||||||
|
* @param l the print listener used to produce output
|
||||||
|
*/
|
||||||
|
public Label(PrintListener l) {
|
||||||
|
this();
|
||||||
|
addPrintListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new label with the specified text and fontweight.
|
||||||
|
*
|
||||||
|
* @param label The text to display
|
||||||
|
* @param fontWeight The fontWeight e.g., Label.BOLD
|
||||||
|
*
|
||||||
|
* @deprecated without direct replacement. Refactor to use
|
||||||
|
* Label(GlobalizedMEssage) instead and modify the theme to
|
||||||
|
* use proper text marking. (Or use setFontWeight separately.
|
||||||
|
*/
|
||||||
|
public Label(String label, String fontWeight) {
|
||||||
|
this(label, true);
|
||||||
|
m_fontWeight = fontWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Label as Text, localized for the current request.
|
||||||
|
*
|
||||||
|
* Although it is not recommended, this method may be overridden to
|
||||||
|
* dynamically generate the text of the label. Overriding code may need
|
||||||
|
* the page state.
|
||||||
|
* <p>
|
||||||
|
* If possible, derived classes should override {@link #getLabel()} instead,
|
||||||
|
* which is called from this method. As long as we don't have a static
|
||||||
|
* method to obtain ApplicationContext, this is a way to get the
|
||||||
|
* RequestContext (to determine the locale). When ApplicationContext gets
|
||||||
|
* available, that will become the suggested way for overriding code to get
|
||||||
|
* context.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @return the string produced for this label
|
||||||
|
*/
|
||||||
|
public String getLabel(PageState state) {
|
||||||
|
return (String) getGlobalizedMessage(state).localize(state.getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * .
|
||||||
|
// *
|
||||||
|
// * This method may be overridden to dynamically generate the default text of
|
||||||
|
// * the label.
|
||||||
|
// *
|
||||||
|
// * @return the string produced for this label.
|
||||||
|
// *
|
||||||
|
// * @deprecated Use {@link #getGlobalizedMessage()}
|
||||||
|
// */
|
||||||
|
// Conflicts with Super's getLabel message of type GlobalizedMessage. But isn't
|
||||||
|
// needed anyway. Should deleted as soon as the refactoring of Label is
|
||||||
|
// completed (i.e. any string Label ironed out).
|
||||||
|
// public String getLabel() {
|
||||||
|
// return getGlobalizedMessage().getKey();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> This should really be getLabel(), but since it was marked STABLE I
|
||||||
|
* can't change its return type. </p>
|
||||||
|
*
|
||||||
|
* @return the default label to display.
|
||||||
|
*/
|
||||||
|
public GlobalizedMessage getGlobalizedMessage() {
|
||||||
|
return getGlobalizedMessage(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p> This should really be getLabel(), but since it was marked STABLE I
|
||||||
|
* can't change its return type. </p>
|
||||||
|
*
|
||||||
|
* @param state the current PageState
|
||||||
|
* @return the label to display for this request, or if state is null, the
|
||||||
|
* default label
|
||||||
|
*/
|
||||||
|
public GlobalizedMessage getGlobalizedMessage(PageState state) {
|
||||||
|
if (state != null) {
|
||||||
|
GlobalizedMessage dynlabel =
|
||||||
|
(GlobalizedMessage) m_requestLabel.get(state);
|
||||||
|
if (dynlabel != null) {
|
||||||
|
return dynlabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets new default text for this Label.
|
||||||
|
*
|
||||||
|
* @param label The new label text; will be used as a key into the current
|
||||||
|
* ResourceBundle if possible, or displayed literally.
|
||||||
|
* @deprecated refactor to use
|
||||||
|
* @see setLabel(GlobalizedMessage) instead!
|
||||||
|
*/
|
||||||
|
public void setLabel(String label) {
|
||||||
|
setLabel(label, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets new request-specific text for this Label to use on this request. If
|
||||||
|
* state is null, then sets the default text instead.
|
||||||
|
*
|
||||||
|
* @param label The new label text; will be used as a key into the current
|
||||||
|
* ResourceBundle if possible, or displayed literally.
|
||||||
|
* @param state the page state
|
||||||
|
* @pre state == null implies !isLocked()
|
||||||
|
* @deprecated refactor to use
|
||||||
|
* @see setLabel(GlobalizedMessage, PageState) instead!
|
||||||
|
*/
|
||||||
|
public void setLabel(String label, PageState state) {
|
||||||
|
if (label == null || label.length() == 0) {
|
||||||
|
label = " ";
|
||||||
|
}
|
||||||
|
// A kind of fallback (or a hack) here. Parameter label is taken as
|
||||||
|
// a key for some (unknown) Resource bundle. Because GlobalizedMessage
|
||||||
|
// will not find a corrresponding message it will display the key
|
||||||
|
// itself, 'faking' a globalized message.
|
||||||
|
setLabel(new GlobalizedMessage(label), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text for this label using a GlobalizedMessage.
|
||||||
|
*
|
||||||
|
* @param label The GlobalizedMessage containing the label text or the
|
||||||
|
* lookup key to use in the ResourceBundle
|
||||||
|
* @param state the current page state; if null, sets the default text for
|
||||||
|
* all requests.
|
||||||
|
* @pre state == null implies !isLocked()
|
||||||
|
*/
|
||||||
|
public void setLabel(GlobalizedMessage label, PageState state) {
|
||||||
|
if (state == null) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_label = label;
|
||||||
|
} else {
|
||||||
|
m_requestLabel.set(state, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default text for this Label.
|
||||||
|
*
|
||||||
|
* Overwrites parent's method an therefore prevents the usage of parent's
|
||||||
|
* label methods (which are attributes, but here it is the content).
|
||||||
|
*
|
||||||
|
* @param label The GlobalizedMessage containing the label text or the
|
||||||
|
* lookup key to use in the ResourceBundle
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setLabel(GlobalizedMessage label) {
|
||||||
|
setLabel(label, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean getOutputEscaping() {
|
||||||
|
return m_escaping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether output is escaped during transformation, by default
|
||||||
|
* true. If true, it will be printed literally, and the user will see
|
||||||
|
* <b>. When false, the browser will interpret as a bold tag.
|
||||||
|
*
|
||||||
|
* @param escaping
|
||||||
|
*/
|
||||||
|
public final void setOutputEscaping(boolean escaping) {
|
||||||
|
m_escaping = escaping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getFontWeight() {
|
||||||
|
return m_fontWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFontWeight(String fontWeight) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_fontWeight = fontWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a print listener. Only one print listener can be set for a label,
|
||||||
|
* since the <code>PrintListener</code> is expected to modify the target
|
||||||
|
* of the <code>PrintEvent</code>.
|
||||||
|
*
|
||||||
|
* @param listener the print listener
|
||||||
|
* @throws IllegalArgumentException if <code>listener</code> is null.
|
||||||
|
* @throws IllegalStateException if a print listener has previously been
|
||||||
|
* added.
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void addPrintListener(PrintListener listener)
|
||||||
|
throws IllegalStateException, IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("Argument listener can not be null");
|
||||||
|
}
|
||||||
|
if (m_printListener != null) {
|
||||||
|
throw new IllegalStateException("Too many listeners. Can only have one");
|
||||||
|
}
|
||||||
|
m_printListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously added print listener. If <code>listener</code> is
|
||||||
|
* not the listener that was added with {@link #addPrintListener
|
||||||
|
* addPrintListener}, an IllegalArgumentException will be thrown.
|
||||||
|
*
|
||||||
|
* @param listener the listener that was added with
|
||||||
|
* <code>addPrintListener</code>
|
||||||
|
* @throws IllegalArgumentException if <code>listener</code> is not the
|
||||||
|
* currently registered print listener or is <code>null</code>.
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void removePrintListener(PrintListener listener)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("listener can not be null");
|
||||||
|
}
|
||||||
|
if (listener != m_printListener) {
|
||||||
|
throw new IllegalArgumentException("listener is not registered with this widget");
|
||||||
|
}
|
||||||
|
m_printListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the (J)DOM fragment for a label.
|
||||||
|
* <p><pre>
|
||||||
|
* <bebop:link href="..." type="..." %bebopAttr;/>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param state The current {@link PageState}.
|
||||||
|
* @param parent The XML element to attach the XML to.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
|
||||||
|
if (!isVisible(state)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label target = firePrintEvent(state);
|
||||||
|
|
||||||
|
Element label = parent.newChildElement("bebop:label", BEBOP_XML_NS);
|
||||||
|
|
||||||
|
target.exportAttributes(label);
|
||||||
|
target.generateDescriptionXML(state, label);
|
||||||
|
|
||||||
|
String weight = target.getFontWeight();
|
||||||
|
if (weight != null && weight.length() > 0) {
|
||||||
|
label.addAttribute("weight", weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target.m_escaping) {
|
||||||
|
label.addAttribute("escape", "yes");
|
||||||
|
} else {
|
||||||
|
label.addAttribute("escape", "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = getGlobalizedMessage()
|
||||||
|
.getKey()
|
||||||
|
.substring(getGlobalizedMessage()
|
||||||
|
.getKey().lastIndexOf(".") + 1);
|
||||||
|
|
||||||
|
// This if clause is needed to prevent printing of keys if the
|
||||||
|
// GlobalizedMessage was created from a String by this class
|
||||||
|
if(!key.equals(target.getLabel(state))) {
|
||||||
|
label.addAttribute("key", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This may break with normal JDOM. We may need to have a node for
|
||||||
|
* the case where there is no weight. The problem comes in that
|
||||||
|
* setText *may* kill the other content in the node. It will kill
|
||||||
|
* the other text, so it may be a good idea anyways.
|
||||||
|
*/
|
||||||
|
label.setText(target.getLabel(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Label firePrintEvent(PageState state) {
|
||||||
|
Label l = this;
|
||||||
|
|
||||||
|
if (m_printListener != null) {
|
||||||
|
try {
|
||||||
|
l = (Label) this.clone();
|
||||||
|
m_printListener.prepare(new PrintEvent(this, state, l));
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Couldn't clone Label for PrintListener. "
|
||||||
|
+ "This probably indicates a serious programming error: "
|
||||||
|
+ e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,821 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.list.ListModel;
|
||||||
|
import com.arsdigita.bebop.list.ListModelBuilder;
|
||||||
|
import com.arsdigita.bebop.list.ListCellRenderer;
|
||||||
|
import com.arsdigita.bebop.list.DefaultListCellRenderer;
|
||||||
|
import com.arsdigita.bebop.event.ActionEvent;
|
||||||
|
import com.arsdigita.bebop.event.ActionListener;
|
||||||
|
import com.arsdigita.bebop.event.ChangeEvent;
|
||||||
|
import com.arsdigita.bebop.event.ChangeListener;
|
||||||
|
import com.arsdigita.bebop.event.EventListenerList;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A <code>List</code>, similar to a <code>javax.swing.JList</code>, that
|
||||||
|
* keeps track of a sequence of items and selections of one or more of
|
||||||
|
* these items. A separate model, {@link ListModel}, is used to represent
|
||||||
|
* the items in the list.
|
||||||
|
*
|
||||||
|
* @see ListModel
|
||||||
|
* @see ListModelBuilder
|
||||||
|
* @see com.arsdigita.bebop.list.ListCellRenderer
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: List.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class List extends SimpleComponent implements BebopConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the StringParameter that the list uses to keep track of
|
||||||
|
* which item is selected.
|
||||||
|
*/
|
||||||
|
public static final String SELECTED = "sel";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the event the list sets when producing links that change
|
||||||
|
* which item is selected.
|
||||||
|
*/
|
||||||
|
public static final String SELECT_EVENT = "s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model builder for this list. Is used to produce a new model for
|
||||||
|
* each request served by this <code>List</code>.
|
||||||
|
* @see #setListModelBuilder
|
||||||
|
* @see ArrayListModelBuilder
|
||||||
|
* @see MapListModelBuilder
|
||||||
|
*/
|
||||||
|
private ListModelBuilder m_modelBuilder;
|
||||||
|
|
||||||
|
private RequestLocal m_model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The renderer used to format list items.
|
||||||
|
* @see DefaultListCellRenderer
|
||||||
|
*/
|
||||||
|
private ListCellRenderer m_renderer;
|
||||||
|
|
||||||
|
private EventListenerList m_listeners;
|
||||||
|
|
||||||
|
private SingleSelectionModel m_selection;
|
||||||
|
|
||||||
|
private ChangeListener m_changeListener;
|
||||||
|
|
||||||
|
private Component m_emptyView;
|
||||||
|
|
||||||
|
private boolean m_stateParamsAreRegistered;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Vertical List layout.</p>
|
||||||
|
**/
|
||||||
|
public static final int VERTICAL = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Horizontal List layout.</p>
|
||||||
|
**/
|
||||||
|
public static final int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
|
||||||
|
private int m_layout = VERTICAL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>List</code> that uses the specified
|
||||||
|
* list model builder to generate
|
||||||
|
* per-request {@link ListModel ListModels}.
|
||||||
|
*
|
||||||
|
* @param b the model builder used for this list
|
||||||
|
* @pre b != null
|
||||||
|
*/
|
||||||
|
public List(ListModelBuilder b) {
|
||||||
|
this();
|
||||||
|
m_modelBuilder = b;
|
||||||
|
m_emptyView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty <code>List</code>.
|
||||||
|
*/
|
||||||
|
public List() {
|
||||||
|
// Force the use of the 'right' constructor
|
||||||
|
this((SingleSelectionModel) null);
|
||||||
|
m_selection =
|
||||||
|
new ParameterSingleSelectionModel(new StringParameter(SELECTED));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty <code>List</code>.
|
||||||
|
*/
|
||||||
|
public List(SingleSelectionModel selection) {
|
||||||
|
// This is the real constructor. All other constructors must call it
|
||||||
|
// directly or indirectly
|
||||||
|
super();
|
||||||
|
m_renderer = new DefaultListCellRenderer();
|
||||||
|
m_listeners = new EventListenerList();
|
||||||
|
m_selection = selection;
|
||||||
|
setListData(new Object[0]);
|
||||||
|
initListModel();
|
||||||
|
m_emptyView = null;
|
||||||
|
m_stateParamsAreRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>List</code> from an array of objects. Uses an
|
||||||
|
* internal {@link ListModelBuilder}. Each {@link ListModel} that is
|
||||||
|
* built will
|
||||||
|
* iterate through the entries of the object, returning the objects from
|
||||||
|
* calls to {@link ListModel#getElement} and the corresponding index,
|
||||||
|
* which is converted to a <code>String</code> from calls to {@link
|
||||||
|
* ListModel#getKey}.
|
||||||
|
*
|
||||||
|
* @param values an array of items
|
||||||
|
* @pre values != null
|
||||||
|
* @see #setListData(Object[] v)
|
||||||
|
*/
|
||||||
|
public List(Object[] values) {
|
||||||
|
this();
|
||||||
|
setListData(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>List</code> from a map. Uses an internal {@link
|
||||||
|
* ListModelBuilder}. Each {@link ListModel} that is built will iterate
|
||||||
|
* through the entries in <code>map</code> in the order in which they are
|
||||||
|
* returned by <code>map.entrySet().iterator()</code>. Calls to {@link
|
||||||
|
* ListModel#getElement} return one value in <code>map</code>. Calls to
|
||||||
|
* {@link ListModel#getElement} return the corresponding key, which is
|
||||||
|
* converted
|
||||||
|
* to a <code>String</code> by calling <code>toString()</code> on the key.
|
||||||
|
*
|
||||||
|
* @param map a key-value mapping for the list items
|
||||||
|
* @pre map != null
|
||||||
|
*/
|
||||||
|
public List(Map map) {
|
||||||
|
this();
|
||||||
|
setListData(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this <code>List</code> and its state parameter(s) with the
|
||||||
|
* specified page.
|
||||||
|
*
|
||||||
|
* @param p the page this list is contained in
|
||||||
|
* @pre p != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void register(Page p) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if ( m_selection.getStateParameter() != null ) {
|
||||||
|
p.addComponentStateParam(this, m_selection.getStateParameter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to a request in which this <code>List</code> was the targetted
|
||||||
|
* component. Calls to this method should only be made through links
|
||||||
|
* generated by this list.
|
||||||
|
*
|
||||||
|
* <p>Determines the new selected element and fires a {@link
|
||||||
|
* ChangeEvent} if it has changed. After that, fires an {@link
|
||||||
|
* ActionEvent}.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @throws ServletException if the control event is unknown.
|
||||||
|
* @pre state != null
|
||||||
|
* @see #fireStateChanged fireStateChanged
|
||||||
|
* @see #fireActionEvent fireActionEvent
|
||||||
|
*/
|
||||||
|
public void respond(PageState state) throws ServletException {
|
||||||
|
String event = state.getControlEventName();
|
||||||
|
|
||||||
|
if ( SELECT_EVENT.equals(event) ) {
|
||||||
|
setSelectedKey(state, state.getControlEventValue());
|
||||||
|
} else {
|
||||||
|
throw new ServletException("Unknown event '" + event + "'");
|
||||||
|
}
|
||||||
|
fireActionEvent(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates XML representing the items in the list. The items are
|
||||||
|
* formatted using a {@link ListCellRenderer}. <code>generateXML</code>
|
||||||
|
* is called on each component returned by the renderer.
|
||||||
|
*
|
||||||
|
* <p> The XML that is generated has the following form:
|
||||||
|
* <pre>
|
||||||
|
* <bebop:list mode="single" %bebopAttr;>
|
||||||
|
* <bebop:cell [selected="selected"] key="itemKey">
|
||||||
|
* ... XML generated for component returned by renderer ...
|
||||||
|
* </bebop:cell>
|
||||||
|
* ... more <bebop:cell> elements, one for each list item ...
|
||||||
|
* </bebop:list></pre>
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @param parent the element into which XML is generated
|
||||||
|
* @pre state != null
|
||||||
|
* @pre parent != null
|
||||||
|
* @see com.arsdigita.bebop.list.ListCellRenderer
|
||||||
|
*/
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
|
||||||
|
if ( ! isVisible(state) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel m = getModel(state);
|
||||||
|
|
||||||
|
// Check if there are items in the list
|
||||||
|
if(m.next()) {
|
||||||
|
|
||||||
|
// The list has items
|
||||||
|
|
||||||
|
Element list = parent.newChildElement(BEBOP_LIST, BEBOP_XML_NS);
|
||||||
|
exportAttributes(list);
|
||||||
|
|
||||||
|
if (m_layout == VERTICAL) {
|
||||||
|
list.addAttribute("layout", "vertical");
|
||||||
|
} else {
|
||||||
|
list.addAttribute("layout", "horizontal");
|
||||||
|
}
|
||||||
|
|
||||||
|
Component c;
|
||||||
|
|
||||||
|
Object selKey;
|
||||||
|
if(getStateParamsAreRegistered())
|
||||||
|
{
|
||||||
|
selKey = getSelectedKey(state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
selKey = null;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
do {
|
||||||
|
Element item = list.newChildElement(BEBOP_CELL, BEBOP_XML_NS);
|
||||||
|
|
||||||
|
String key = m.getKey();
|
||||||
|
Assert.exists(key);
|
||||||
|
|
||||||
|
// Converting both keys to String for comparison
|
||||||
|
// since ListModel.getKey returns a String
|
||||||
|
boolean selected = (selKey != null) &&
|
||||||
|
key.equals(selKey.toString());
|
||||||
|
|
||||||
|
item.addAttribute("key", key);
|
||||||
|
if ( selected ) {
|
||||||
|
item.addAttribute("selected", "selected");
|
||||||
|
}
|
||||||
|
if(getStateParamsAreRegistered())
|
||||||
|
state.setControlEvent(this, SELECT_EVENT, key);
|
||||||
|
c = getCellRenderer().getComponent(this, state, m.getElement(),
|
||||||
|
key, i, selected);
|
||||||
|
c.generateXML(state, item);
|
||||||
|
i += 1;
|
||||||
|
} while (m.next());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The list has no items
|
||||||
|
if(m_emptyView != null) {
|
||||||
|
// Display the empty view
|
||||||
|
m_emptyView.generateXML(state, parent);
|
||||||
|
} else {
|
||||||
|
// For compatibility reasons, generate an empty
|
||||||
|
// list element. In the future, this should go away
|
||||||
|
Element list = parent.newChildElement(BEBOP_LIST, BEBOP_XML_NS);
|
||||||
|
exportAttributes(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.clearControlEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Retrieve the current List layout.</p>
|
||||||
|
*
|
||||||
|
* @return List.VERTICAL or List.HORIZONTAL
|
||||||
|
**/
|
||||||
|
public int getLayout() {
|
||||||
|
return m_layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Set the current List layout.</p>
|
||||||
|
*
|
||||||
|
* @param layout New layout value, must be List.VERTICAL or List.HORIZONTAL
|
||||||
|
**/
|
||||||
|
public void setLayout(int layout) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
Assert.isTrue((layout == VERTICAL) || (layout == HORIZONTAL),
|
||||||
|
"Invalid layout code passed to setLayout");
|
||||||
|
m_layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is part of a mechanism to freakishly allow
|
||||||
|
* List's to be used as parent classes for components
|
||||||
|
* that do not have their state params registered with the
|
||||||
|
* page. An example of a situation like this is Form ErrorDisplay
|
||||||
|
* being used in a Metaform
|
||||||
|
*/
|
||||||
|
public void setStateParamsAreRegistered(boolean val)
|
||||||
|
{
|
||||||
|
m_stateParamsAreRegistered = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getStateParamsAreRegistered()
|
||||||
|
{
|
||||||
|
return m_stateParamsAreRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the renderer currently used for rendering list items.
|
||||||
|
*
|
||||||
|
* @return the current list cell renderer.
|
||||||
|
* @see #setCellRenderer setCellRenderer
|
||||||
|
* @see com.arsdigita.bebop.list.ListCellRenderer
|
||||||
|
*/
|
||||||
|
public final ListCellRenderer getCellRenderer() {
|
||||||
|
return m_renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the cell renderer to be used when generating output with
|
||||||
|
* or {@link #generateXML generateXML}.
|
||||||
|
*
|
||||||
|
* @param r a <code>ListCellRenderer</code> value
|
||||||
|
* @pre r != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
* @see com.arsdigita.bebop.list.ListCellRenderer
|
||||||
|
*/
|
||||||
|
public final void setCellRenderer(ListCellRenderer r) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_renderer = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the model builder currently used to build each request-specific
|
||||||
|
* {@link ListModel}.
|
||||||
|
*
|
||||||
|
* @return a <code>ListModelBuilder</code> value.
|
||||||
|
* @see #setModelBuilder setModelBuilder
|
||||||
|
* @see ListModelBuilder
|
||||||
|
*/
|
||||||
|
public final ListModelBuilder getModelBuilder() {
|
||||||
|
return m_modelBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the model builder used to build each request-specific
|
||||||
|
* {@link ListModel}.
|
||||||
|
*
|
||||||
|
* @param b a <code>ListModelBuilder</code> value
|
||||||
|
* @pre ! isLocked()
|
||||||
|
* @see ListModelBuilder
|
||||||
|
*/
|
||||||
|
public final void setModelBuilder(ListModelBuilder b) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_modelBuilder = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the empty view component, which is
|
||||||
|
* shown if there are no items in the list. This component must
|
||||||
|
* be stateless. For example, it could be an Image or a Label.
|
||||||
|
*
|
||||||
|
* @param c the new empty view component
|
||||||
|
*/
|
||||||
|
public final void setEmptyView(Component c) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_emptyView = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the empty view component. The empty view component is
|
||||||
|
* shown if there are no items in the list.
|
||||||
|
* @return the empty view component.
|
||||||
|
*/
|
||||||
|
public final Component getEmptyView() {
|
||||||
|
return m_emptyView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the private <code>m_model</code> variable. The initial
|
||||||
|
* value is what the model builder returns for the state.
|
||||||
|
*/
|
||||||
|
private void initListModel() {
|
||||||
|
m_model = new RequestLocal() {
|
||||||
|
protected Object initialValue(PageState s) {
|
||||||
|
return getModelBuilder().makeModel(List.this, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list model used in processing the request represented by
|
||||||
|
* <code>state</code>.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return the list model used in processing the request represented by
|
||||||
|
* <code>state</code>.
|
||||||
|
*/
|
||||||
|
public ListModel getModel(PageState state) {
|
||||||
|
return (ListModel) m_model.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list to use for the values in <code>values</code>. Each {@link
|
||||||
|
* ListModel} that is built will iterate through the entries of the object,
|
||||||
|
* returning the objects from calls to {@link ListModel#getElement} and
|
||||||
|
* the corresponding index, which is converted to a <code>String</code>
|
||||||
|
* from calls to {@link ListModel#getKey}.
|
||||||
|
*
|
||||||
|
* @param values an array of items
|
||||||
|
* @pre values != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setListData(Object[] values) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_modelBuilder = new ArrayListModelBuilder(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list to use the entries in <code>map</code>. Each {@link
|
||||||
|
* ListModel} that is built will iterate through the entries in
|
||||||
|
* <code>map</code> in the order in which they are returned by
|
||||||
|
* <code>map.entrySet().iterator()</code>. Calls to {@link
|
||||||
|
* ListModel#getElement} return one value in <code>map</code>. Calls to
|
||||||
|
* {@link ListModel#getElement} return the corresponding key, which is
|
||||||
|
* converted to a <code>String</code> by calling <code>toString()</code>
|
||||||
|
* on the key.
|
||||||
|
* @param map a key-value mapping for the list items
|
||||||
|
* @pre map != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void setListData(Map map) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_modelBuilder = new MapListModelBuilder(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the selection model. The model keeps track of which list item is
|
||||||
|
* currently selected, and can be used to manipulate the selection
|
||||||
|
* programmatically.
|
||||||
|
*
|
||||||
|
* @return the model used by the list to keep track of the selected list
|
||||||
|
* item.
|
||||||
|
*/
|
||||||
|
public final SingleSelectionModel getSelectionModel() {
|
||||||
|
return m_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selection model that the list uses to keep track of the
|
||||||
|
* currently selected list item.
|
||||||
|
*
|
||||||
|
* @param m the new selection model
|
||||||
|
* @pre m != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public final void setSelectionModel(SingleSelectionModel m) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if ( m_changeListener != null ) {
|
||||||
|
// Transfer the change listener
|
||||||
|
m_selection.removeChangeListener(m_changeListener);
|
||||||
|
m.addChangeListener(m_changeListener);
|
||||||
|
}
|
||||||
|
m_selection = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key for the selected list item. This will only
|
||||||
|
* be a valid key
|
||||||
|
* if {@link #isSelected isSelected} is <code>true</code>.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return the key for the selected list item.
|
||||||
|
* @pre isSelected(state)
|
||||||
|
*/
|
||||||
|
public Object getSelectedKey(PageState state) {
|
||||||
|
return m_selection.getSelectedKey(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selection to the one with the specified key. If
|
||||||
|
* <code>key</code> was not already selected, fires the {@link
|
||||||
|
* ChangeEvent}.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @param key the key for the selected list item
|
||||||
|
* @see #fireStateChanged fireStateChanged
|
||||||
|
*/
|
||||||
|
public void setSelectedKey(PageState state, String key) {
|
||||||
|
m_selection.setSelectedKey(state, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if one of the list items is currently selected.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return <code>true</code> if one of the list items is selected
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isSelected(PageState state) {
|
||||||
|
return m_selection.isSelected(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the selection in the request represented by <code>state</code>.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @post ! isSelected(state)
|
||||||
|
*/
|
||||||
|
public void clearSelection(PageState state) {
|
||||||
|
m_selection.clearSelection(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the change listener that is used for forwarding change events
|
||||||
|
* fired by
|
||||||
|
* the selection model to change listeners registered with the list. The
|
||||||
|
* returned change listener refires the event with the list,
|
||||||
|
* rather than the selection model, as source.
|
||||||
|
*
|
||||||
|
* @return the change listener used internally by the list.
|
||||||
|
*/
|
||||||
|
protected ChangeListener createChangeListener() {
|
||||||
|
return new ChangeListener() {
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
fireStateChanged(e.getPageState());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a change listener. A change event is fired whenever the selected
|
||||||
|
* list item changes during the processing of a request. The change event
|
||||||
|
* that listeners receive names the list as the source.
|
||||||
|
*
|
||||||
|
* @param l the change listener to run when the selected item changes in
|
||||||
|
* a request
|
||||||
|
* @pre ! isLocked()
|
||||||
|
*/
|
||||||
|
public void addChangeListener(ChangeListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if ( m_changeListener == null ) {
|
||||||
|
m_changeListener = createChangeListener();
|
||||||
|
m_selection.addChangeListener(m_changeListener);
|
||||||
|
}
|
||||||
|
m_listeners.add(ChangeListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a change listener. The listener should have been previously
|
||||||
|
* added with {@link #addChangeListener addChangeListener}, although no
|
||||||
|
* error is signalled if the change listener is not found among the
|
||||||
|
* list's listeners.
|
||||||
|
*
|
||||||
|
* @param l the change listener to remove from the list
|
||||||
|
*/
|
||||||
|
public void removeChangeListener(ChangeListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(ChangeListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires a change event to signal that the selected list item has changed
|
||||||
|
* in the request represented by <code>state</code>. The source of the
|
||||||
|
* event is the list.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
*/
|
||||||
|
protected void fireStateChanged(PageState state) {
|
||||||
|
Iterator
|
||||||
|
i=m_listeners.getListenerIterator(ChangeListener.class);
|
||||||
|
ChangeEvent e = null;
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
if ( e == null ) {
|
||||||
|
e = new ChangeEvent(this, state);
|
||||||
|
}
|
||||||
|
((ChangeListener) i.next()).stateChanged(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action events
|
||||||
|
/**
|
||||||
|
* Adds an action listener. This method is run whenever {@link #respond respond} is
|
||||||
|
* called on the list. This gives clients a way to track mouse clicks
|
||||||
|
* received by the list.
|
||||||
|
* @param 1 the action listener to add
|
||||||
|
*
|
||||||
|
* @pre l != null
|
||||||
|
* @pre ! isLocked()
|
||||||
|
* @see #respond respond
|
||||||
|
*/
|
||||||
|
public void addActionListener(ActionListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.add(ActionListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a previously added action listener.
|
||||||
|
* @param 1 the action listener to remove
|
||||||
|
*
|
||||||
|
* @see #addActionListener addActionListener
|
||||||
|
*/
|
||||||
|
public void removeActionListener(ActionListener l) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(ActionListener.class, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires an action event signalling that the list received the request
|
||||||
|
* submission. All registered action listeners are run. The source of
|
||||||
|
* the event is the list.
|
||||||
|
* @param state the state of the current request
|
||||||
|
*
|
||||||
|
* @pre state != null
|
||||||
|
* @see #respond respond
|
||||||
|
*/
|
||||||
|
protected void fireActionEvent(PageState state) {
|
||||||
|
Iterator
|
||||||
|
i=m_listeners.getListenerIterator(ActionListener.class);
|
||||||
|
ActionEvent e = null;
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
if ( e == null ) {
|
||||||
|
e = new ActionEvent(this, state);
|
||||||
|
}
|
||||||
|
((ActionListener) i.next()).actionPerformed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListModelBuilder for maps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build list models from a map. The list models use the result of
|
||||||
|
* <code>toString()</code> called on the key of the map entries as their
|
||||||
|
* keys and return the associated value as the element for the list items
|
||||||
|
* the list model iterates over.
|
||||||
|
*/
|
||||||
|
private static class MapListModelBuilder implements ListModelBuilder {
|
||||||
|
private Map m_map;
|
||||||
|
private boolean m_locked;
|
||||||
|
|
||||||
|
public MapListModelBuilder() {
|
||||||
|
this(Collections.EMPTY_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapListModelBuilder(Map m) {
|
||||||
|
m_map = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListModel makeModel(List l, PageState state) {
|
||||||
|
return new ListModel() {
|
||||||
|
private Iterator i = m_map.entrySet().iterator();
|
||||||
|
private Map.Entry e = null;
|
||||||
|
|
||||||
|
public boolean next() {
|
||||||
|
if ( ! i.hasNext() ) {
|
||||||
|
e = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
e = (Map.Entry) i.next();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getElement() {
|
||||||
|
checkState();
|
||||||
|
return e.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
checkState();
|
||||||
|
return e.getKey().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkState() {
|
||||||
|
if ( e == null ) {
|
||||||
|
throw new IllegalStateException("No valid current item. "
|
||||||
|
+ "Model is either before first item or after last item");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lock() {
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isLocked() {
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListModelBuilder for arrays
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build list models from an array of values. The list models use the
|
||||||
|
* index of the array entries, converted to a <code>String</code>, as the
|
||||||
|
* key for the list items and the array values as their elements.
|
||||||
|
*/
|
||||||
|
private static class ArrayListModelBuilder implements ListModelBuilder {
|
||||||
|
private Object[] m_values;
|
||||||
|
private boolean m_locked;
|
||||||
|
|
||||||
|
public ArrayListModelBuilder() {
|
||||||
|
this(new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayListModelBuilder(Object[] values) {
|
||||||
|
m_values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListModel makeModel(List l, PageState state) {
|
||||||
|
return new ListModel() {
|
||||||
|
private int i = -1;
|
||||||
|
|
||||||
|
public boolean next() {
|
||||||
|
i += 1;
|
||||||
|
return ( i < m_values.length );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getElement() {
|
||||||
|
checkState();
|
||||||
|
return m_values[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
checkState();
|
||||||
|
return String.valueOf(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkState() {
|
||||||
|
if ( i < 0 ) {
|
||||||
|
throw new IllegalStateException
|
||||||
|
("Before first item. Call next() first.");
|
||||||
|
}
|
||||||
|
if ( i >= m_values.length ) {
|
||||||
|
throw new IllegalStateException("After last item. Model exhausted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lock() {
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isLocked() {
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ListModel} that has no rows.
|
||||||
|
*/
|
||||||
|
public static final ListModel EMPTY_MODEL = new ListModel() {
|
||||||
|
public boolean next() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
throw new IllegalStateException("ListModel is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getElement() {
|
||||||
|
throw new IllegalStateException("ListModel is empty");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.list.ListModel;
|
||||||
|
import com.arsdigita.bebop.list.ListModelBuilder;
|
||||||
|
import com.arsdigita.bebop.list.ListCellRenderer;
|
||||||
|
import com.arsdigita.util.LockableImpl;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays validation errors for the page. These might have occured due to validation listeners on
|
||||||
|
* some state parameters within the page.
|
||||||
|
*
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @version $Id: PageErrorDisplay.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class PageErrorDisplay extends List {
|
||||||
|
|
||||||
|
private static final String COLOR = "color";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>PageErrorDisplay</code>.
|
||||||
|
*/
|
||||||
|
public PageErrorDisplay() {
|
||||||
|
this(new PageErrorModelBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>PageErrorDisplay</code> from the errors supplied by a list model
|
||||||
|
* builder.
|
||||||
|
*
|
||||||
|
* @param builder the {@link ListModelBuilder} that will supply the errors
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected PageErrorDisplay(ListModelBuilder builder) {
|
||||||
|
super(builder);
|
||||||
|
setCellRenderer(new LabelCellRenderer());
|
||||||
|
setTextColor("red");
|
||||||
|
setClassAttr("pageErrorDisplay");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the HTML color of the error messages.
|
||||||
|
*
|
||||||
|
* @param c An HTML color, such as "#99CCFF" or "red"
|
||||||
|
*/
|
||||||
|
public void setTextColor(String c) {
|
||||||
|
setAttribute(COLOR, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HTML color of the error messages.
|
||||||
|
*
|
||||||
|
* @return the HTML color of the error messages.
|
||||||
|
*/
|
||||||
|
public String getTextColor() {
|
||||||
|
return getAttribute(COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if there are errors to display.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if there are any errors to display; <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
protected boolean hasErrors(PageState state) {
|
||||||
|
return (state.getErrors().hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML for this component. If the state has no errors in it, does not generate any
|
||||||
|
* XML.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @param parent the parent XML element
|
||||||
|
*/
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
if (hasErrors(state)) {
|
||||||
|
super.generateXML(state, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A private class which builds a ListModel based on form errors
|
||||||
|
private static class PageErrorModelBuilder extends LockableImpl
|
||||||
|
implements ListModelBuilder {
|
||||||
|
|
||||||
|
public PageErrorModelBuilder() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListModel makeModel(List l, PageState state) {
|
||||||
|
return new StringIteratorModel(state.getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ListModel which generates items based on an Iterator
|
||||||
|
protected static class StringIteratorModel implements ListModel {
|
||||||
|
|
||||||
|
private Iterator m_iter;
|
||||||
|
private GlobalizedMessage m_error;
|
||||||
|
private int m_i;
|
||||||
|
|
||||||
|
public StringIteratorModel(Iterator iter) {
|
||||||
|
m_iter = iter;
|
||||||
|
m_error = null;
|
||||||
|
m_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean next() {
|
||||||
|
if (!m_iter.hasNext()) {
|
||||||
|
m_i = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_error = (GlobalizedMessage) m_iter.next();
|
||||||
|
++m_i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkState() {
|
||||||
|
if (m_i == 0) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"next() has not been called succesfully"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getElement() {
|
||||||
|
checkState();
|
||||||
|
return m_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
checkState();
|
||||||
|
return Integer.toString(m_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ListCellRenderer that renders labels
|
||||||
|
private static class LabelCellRenderer implements ListCellRenderer {
|
||||||
|
|
||||||
|
public Component getComponent(List list, PageState state, Object value,
|
||||||
|
String key, int index, boolean isSelected) {
|
||||||
|
return new Label((GlobalizedMessage) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link SingleSelectionModel} that uses
|
||||||
|
* a state parameter for managing the currently selected key.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* A typical use case for this class is as follows.
|
||||||
|
* <blockquote><pre><code>public TheConstructor() {
|
||||||
|
* m_parameter = new StringParameter("my_key");
|
||||||
|
* m_sel = new ParameterSingleSelectionModel(m_parameter);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void register(Page p) {
|
||||||
|
* p.addComponent(this);
|
||||||
|
* p.addComponentStateParam(this, m_param);
|
||||||
|
* }</code></pre></blockquote>
|
||||||
|
*
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @version $Id: ParameterSingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class ParameterSingleSelectionModel
|
||||||
|
extends AbstractSingleSelectionModel {
|
||||||
|
|
||||||
|
|
||||||
|
private ParameterModel m_parameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ParameterSingleSelectionModel.
|
||||||
|
*
|
||||||
|
* @param m the parameter model that will be used to
|
||||||
|
* keep track of the currently selected key
|
||||||
|
*/
|
||||||
|
public ParameterSingleSelectionModel(ParameterModel m) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
m_parameter = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key that identifies the selected element.
|
||||||
|
*
|
||||||
|
* @param state a <code>PageState</code> value
|
||||||
|
* @return a <code>String</code> value.
|
||||||
|
*/
|
||||||
|
public Object getSelectedKey(PageState state) {
|
||||||
|
final FormModel model = state.getPage().getStateModel();
|
||||||
|
if (model.containsFormParam(m_parameter)) {
|
||||||
|
return state.getValue(m_parameter);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ParameterModel getStateParameter() {
|
||||||
|
return m_parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected key.
|
||||||
|
*
|
||||||
|
* @param state represents the state of the current request
|
||||||
|
* @param newKey the new selected key
|
||||||
|
*/
|
||||||
|
public void setSelectedKey(PageState state, Object newKey) {
|
||||||
|
final Object oldKey = getSelectedKey(state);
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
final FormModel model = state.getPage().getStateModel();
|
||||||
|
Assert.isTrue(model.containsFormParam(m_parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.setValue(m_parameter, newKey);
|
||||||
|
|
||||||
|
if (newKey == null && oldKey == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newKey != null && newKey.equals(oldKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable whose value is local to each request. Objects that need to store
|
||||||
|
* values that change in every request should declare them to be
|
||||||
|
* <code>RequestLocal</code>. These variables hold their values only during a
|
||||||
|
* duration of a request. They get reinitialized by a call to {@link
|
||||||
|
* #initialValue(PageState)} for every new HTTP request.
|
||||||
|
*
|
||||||
|
* <p> For example, a class that wants to implement a request local property
|
||||||
|
* <code>foo</code> would do the following:</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class SomeClass {
|
||||||
|
* private RequestLocal m_foo;
|
||||||
|
*
|
||||||
|
* public SomeClass() {
|
||||||
|
* m_foo = new RequestLocal() {
|
||||||
|
* protected Object initialValue(PageState s) {
|
||||||
|
* // Foo could be a much more complicated value
|
||||||
|
* return s.getRequestURI();
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public String getFoo(PageState s) {
|
||||||
|
* return (String) m_foo.get(s);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void setFoo(PageState s, String v) {
|
||||||
|
* m_foo.set(s, v);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class RequestLocal {
|
||||||
|
|
||||||
|
private static final String ATTRIBUTE_KEY =
|
||||||
|
"com.arsdigita.bebop.RequestLocal";
|
||||||
|
|
||||||
|
// Fetch the map used to store RequestLocals, possibly creating it along the
|
||||||
|
// way
|
||||||
|
private Map getMap(HttpServletRequest request) {
|
||||||
|
// This lock is paranoid. We can remove it if we know that only one
|
||||||
|
// thread will be touching a request object at a time. (Seems likely,
|
||||||
|
// but, like I said, I'm paranoid.)
|
||||||
|
synchronized (request) {
|
||||||
|
Map result = (Map)request.getAttribute(ATTRIBUTE_KEY);
|
||||||
|
result = (Map)request.getAttribute(ATTRIBUTE_KEY);
|
||||||
|
if (result == null) {
|
||||||
|
result = new HashMap();
|
||||||
|
request.setAttribute(ATTRIBUTE_KEY, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value to be used during the request represented by
|
||||||
|
* <code>state</code>. This method is called at most once per request,
|
||||||
|
* the first time the value of this <code>RequestLocal</code> is
|
||||||
|
* requested with {@link #get get}. <code>RequestLocal</code> must be
|
||||||
|
* subclassed, and this method must be overridden. Typically, an
|
||||||
|
* anonymous inner class will be used.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param state represents the current state of the request
|
||||||
|
* @return the initial value for this request local variable.
|
||||||
|
*/
|
||||||
|
protected Object initialValue(PageState state) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request-specific value for this variable for the request
|
||||||
|
* associated with <code>state</code>.
|
||||||
|
*
|
||||||
|
* @param state represents the current state of the request
|
||||||
|
* @return the value for this request local variable.
|
||||||
|
*/
|
||||||
|
public Object get(PageState state) {
|
||||||
|
Map map = getMap(state.getRequest());
|
||||||
|
Object result = map.get(this);
|
||||||
|
|
||||||
|
if ( result == null && !map.containsKey(this) ) {
|
||||||
|
result = initialValue(state);
|
||||||
|
set(state, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new value for the request local variable and associates it with
|
||||||
|
* the request represented by <code>state</code>.
|
||||||
|
*
|
||||||
|
* @param state represents the current state of the request
|
||||||
|
* @param value the new value for this request local variable
|
||||||
|
*/
|
||||||
|
public void set(PageState state, Object value) {
|
||||||
|
set(state.getRequest(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a new value for the request local variable and associates it with
|
||||||
|
* the request represented by <code>request</code></p>
|
||||||
|
*
|
||||||
|
* <p>This method is intended for use when a Dispatcher needs to assign some
|
||||||
|
* value to a RequestLocal for Bebop Page processing before Page processing
|
||||||
|
* begins.</p>
|
||||||
|
*
|
||||||
|
* @param request represents the current request
|
||||||
|
* @param value the new value for this request local variable
|
||||||
|
*/
|
||||||
|
public void set(HttpServletRequest request, Object value) {
|
||||||
|
getMap(request).put(this, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @version $Id: SessionExpiredException.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class SessionExpiredException extends ServletException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.util.Attributes;
|
||||||
|
import com.arsdigita.kernel.KernelConfig;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple implementation of the Component interface.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Uday Mathur
|
||||||
|
*
|
||||||
|
* @version $Id: SimpleComponent.java 1498 2007-03-19 16:22:15Z apevec $
|
||||||
|
*/
|
||||||
|
public class SimpleComponent extends Completable
|
||||||
|
implements Component, Cloneable {
|
||||||
|
|
||||||
|
private boolean m_locked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Attribute object is protected to make it easier for the Form Builder
|
||||||
|
* service to persist the SimpleComponent.
|
||||||
|
* Locking violation is not a problem since if the SimpleComponent is locked
|
||||||
|
* then the Attribute object will also be locked.
|
||||||
|
*/
|
||||||
|
protected Attributes m_attr;
|
||||||
|
|
||||||
|
private String m_key = null; // name mangling key
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a component. The clone is not locked and has its own set of
|
||||||
|
* attributes.
|
||||||
|
* @return the clone of a component.
|
||||||
|
* @throws java.lang.CloneNotSupportedException
|
||||||
|
* @post ! ((SimpleComponent) return).isLocked()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
SimpleComponent result = (SimpleComponent) super.clone();
|
||||||
|
if ( m_attr != null ) {
|
||||||
|
result.m_attr = (Attributes) m_attr.clone();
|
||||||
|
}
|
||||||
|
result.m_locked = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers state parameters for the page with its model. Documentation
|
||||||
|
* from Interface Componment:
|
||||||
|
*
|
||||||
|
* A simple component with a state parameter <code>param</code> would do
|
||||||
|
* the following in the body of this method:
|
||||||
|
* <pre>
|
||||||
|
* p.addComponent(this);
|
||||||
|
* p.addComponentStateParam(this, param);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* You should override this method to set the default visibility
|
||||||
|
* of your component:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public void register(Page p) {
|
||||||
|
* super.register(p);
|
||||||
|
* p.setVisibleDefault(childNotInitiallyShown,false);
|
||||||
|
* p.setVisibleDefault(anotherChild, false);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Always call <code>super.register</code> when you override
|
||||||
|
* <code>register</code>. Otherwise your component may
|
||||||
|
* malfunction and produce errors like "Widget ... isn't
|
||||||
|
* associated with any Form"
|
||||||
|
*
|
||||||
|
* @pre p != null
|
||||||
|
* @param p
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void register(Page p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers form parameters with the form model for this form.
|
||||||
|
* This method is only important for {@link FormSection form sections}
|
||||||
|
* and {@link com.arsdigita.bebop.form.Widget widgets} (components that
|
||||||
|
* have a connection to an HTML form). Other components can implement it
|
||||||
|
* as a no-op.
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* @param m
|
||||||
|
* @pre f != null
|
||||||
|
* @pre m != null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void register(Form f, FormModel m) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does processing that is special to the component
|
||||||
|
* receiving the click.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void respond(PageState state)
|
||||||
|
throws javax.servlet.ServletException { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator children() {
|
||||||
|
return Collections.EMPTY_LIST.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adds [J]DOM nodes for this component. Specifically for
|
||||||
|
* base class SimpleComponent, does nothing.
|
||||||
|
* @param p
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isLocked() {
|
||||||
|
return m_locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lock () {
|
||||||
|
if (m_attr != null) {
|
||||||
|
m_attr.lock();
|
||||||
|
}
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks this component. Package visibility is intentional; the
|
||||||
|
* only time a component should be unlocked is when it's pooled and
|
||||||
|
* gets locked because it's put into a page. It needs to be unlocked
|
||||||
|
* when the instance is recycled.
|
||||||
|
*/
|
||||||
|
void unlock() {
|
||||||
|
m_locked = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Working with standard component attributes */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class attribute.
|
||||||
|
* @return the class attribute.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getClassAttr() {
|
||||||
|
return getAttribute(CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the class attribute.
|
||||||
|
* @param theClass a valid <a
|
||||||
|
* href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name">XML name</a>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setClassAttr(String theClass) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(CLASS, theClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the style attribute.
|
||||||
|
* @return the style attribute.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getStyleAttr() {
|
||||||
|
return getAttribute(STYLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the style attribute. <code>style</code> should be a valid CSS
|
||||||
|
* style, since its value will be copied verbatim to the output and
|
||||||
|
* appear as a <tt>style</tt> attribute in the top level XML or HTML
|
||||||
|
* output element.
|
||||||
|
*
|
||||||
|
* @param style a valid CSS style description for use in the
|
||||||
|
* <tt>style</tt> attribute of an HTML tag
|
||||||
|
* @see <a href="#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setStyleAttr(String style) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(STYLE, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the <tt>id</tt> attribute.
|
||||||
|
* @return the <tt>id</tt> attribute.
|
||||||
|
* @see #setIdAttr(String id)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getIdAttr() {
|
||||||
|
return getAttribute(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>id</tt> attribute. <code>id</code>
|
||||||
|
* should be an <a
|
||||||
|
* href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name">XML name</a>
|
||||||
|
* that is unique within the {@link Page Page} in which this component is
|
||||||
|
* contained. The value of <code>id</code> is copied literally to the
|
||||||
|
* output and not used for internal processing.
|
||||||
|
*
|
||||||
|
* @param id a valid XML identifier
|
||||||
|
* @see <a href="#standard">Standard Attributes</a>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setIdAttr(String id) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute(ID, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Methods for attribute management */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an attribute. Overwrites any old values. These values are used to
|
||||||
|
* generate attributes for the top level XML or HTML element that is
|
||||||
|
* output from this component with {@link #generateXML generateXML}.
|
||||||
|
*
|
||||||
|
* @pre name != null
|
||||||
|
* @post getAttribute(name) == value
|
||||||
|
*
|
||||||
|
* @param name attribute name, case insensitive
|
||||||
|
* @param value new attribute value
|
||||||
|
*/
|
||||||
|
final protected void setAttribute(String name, String value) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if (m_attr == null) {
|
||||||
|
m_attr = new Attributes();
|
||||||
|
}
|
||||||
|
m_attr.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of an attribute.
|
||||||
|
*
|
||||||
|
* @pre name != null
|
||||||
|
*
|
||||||
|
* @param name attribute name, case insensitive
|
||||||
|
* @return the string value previously set with {@link #setAttribute
|
||||||
|
* setAttribute}, or <code>null</code> if none was set.
|
||||||
|
* @see #setAttribute
|
||||||
|
*/
|
||||||
|
final protected String getAttribute(String name) {
|
||||||
|
return (m_attr == null) ? null : m_attr.getAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the attributes set with {@link #setAttribute setAttribute} to the
|
||||||
|
* element <code>target</code>. The attributes set with
|
||||||
|
* <code>exportAttributes</code> overwrite attributes with identical names
|
||||||
|
* that <code>target</code> might already have.
|
||||||
|
*
|
||||||
|
* @pre target != null
|
||||||
|
*
|
||||||
|
* @param target element to which attributes are added
|
||||||
|
* @see #setAttribute
|
||||||
|
*/
|
||||||
|
final protected void exportAttributes(com.arsdigita.xml.Element target) {
|
||||||
|
if (m_attr != null) {
|
||||||
|
m_attr.exportAttributes(target);
|
||||||
|
}
|
||||||
|
if (KernelConfig.getConfig().isDebugEnabled() ||
|
||||||
|
Bebop.getConfig().showClassName()) {
|
||||||
|
target.addAttribute("bebop:classname", getClass().getName(),
|
||||||
|
BEBOP_XML_NS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if any attributes have been set.
|
||||||
|
* @return <code>true</code> if any attributes have been set;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
final protected boolean hasAttributes() {
|
||||||
|
return m_attr != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set an arbitrary meta data attribute on the component.
|
||||||
|
* The name of the attribute in the XML will be prefixed
|
||||||
|
* with the string 'metadata.'
|
||||||
|
*/
|
||||||
|
final public void setMetaDataAttribute(String name, String value) {
|
||||||
|
setAttribute("metadata." + name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public String getMetaDataAttribute(String name) {
|
||||||
|
return getAttribute("metadata." + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplies a key for parameter name mangling.
|
||||||
|
*
|
||||||
|
* @param key the key to mangle
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Component setKey(String key) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if (key.charAt(0) >= 0 && key.charAt(0) <= 9) {
|
||||||
|
throw new IllegalArgumentException("key \"" + key + "\" must not start with a digit.");
|
||||||
|
}
|
||||||
|
m_key = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a key for parameter name mangling.
|
||||||
|
* @return a key for parameter name mangling.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final String getKey() {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVisible(PageState s) {
|
||||||
|
return s.isVisible(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(PageState s, boolean v) {
|
||||||
|
s.setVisible(this, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic implementation of the {@link Container} interface which, by default,
|
||||||
|
* renders all of its children directly, without wrapping them in any kind of
|
||||||
|
* tag.
|
||||||
|
*
|
||||||
|
* However, the {@link #SimpleContainer(String, String)} constructor and/or the
|
||||||
|
* {@link #setTag(String)} method can be used to cause the container to wrap
|
||||||
|
* the XML for its children in an arbitrary tag. This functionality is useful
|
||||||
|
* for XSL templating.
|
||||||
|
*
|
||||||
|
* For example, a template rule might be written to arrange the children of this
|
||||||
|
* component in paragraphs:
|
||||||
|
*
|
||||||
|
* <blockquote><pre><code>
|
||||||
|
* // Java Code:
|
||||||
|
* m_container = new SimpleContainer("cms:foo", CMS_XML_NS);
|
||||||
|
*
|
||||||
|
* // XSL code:
|
||||||
|
* <xsl:template match="cms:foo">
|
||||||
|
* <xsl:for-each select="*">
|
||||||
|
* <p>
|
||||||
|
* <xsl:apply-templates select="."/>
|
||||||
|
* </p>
|
||||||
|
* </xsl:for-each>
|
||||||
|
* </xsl:template>
|
||||||
|
* </code></pre></blockquote>
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Uday Mathur
|
||||||
|
*
|
||||||
|
* @version $Id: SimpleContainer.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public class SimpleContainer extends SimpleComponent implements Container {
|
||||||
|
|
||||||
|
private List m_components;
|
||||||
|
private String m_tag, m_ns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty <code>SimpleContainer</code>.
|
||||||
|
*/
|
||||||
|
public SimpleContainer() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty <code>SimpleContainer</code> that will
|
||||||
|
* wrap its children in the specified tag.
|
||||||
|
*
|
||||||
|
* @param tag the name of the XML element that will be used to wrap the
|
||||||
|
* children of this container
|
||||||
|
* @param ns the namespace for the tag
|
||||||
|
*/
|
||||||
|
public SimpleContainer(String tag, String ns) {
|
||||||
|
super();
|
||||||
|
m_components = new ArrayList();
|
||||||
|
m_tag = tag;
|
||||||
|
m_ns = ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component to this container.
|
||||||
|
*
|
||||||
|
* @param pc the component to be added
|
||||||
|
*/
|
||||||
|
public void add(Component pc) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
Assert.exists(pc);
|
||||||
|
m_components.add(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a component to this container.
|
||||||
|
*
|
||||||
|
* @param pc the component to be added
|
||||||
|
* @param constraints this parameter is ignored. Child classes should
|
||||||
|
* override the add method if they wish to provide special handling
|
||||||
|
* of constraints.
|
||||||
|
*/
|
||||||
|
public void add(Component c, int constraints) {
|
||||||
|
add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines membership.
|
||||||
|
* @return <code>true</code> if the specified object is in this container;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
* @param o the object type, typically a component. Type
|
||||||
|
* Object allows slicker code when o comes from any kind of collection.
|
||||||
|
*/
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return m_components.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the container is empty.
|
||||||
|
*
|
||||||
|
* @return <code>false</code> if the container has any children;
|
||||||
|
* <code>true</code> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return m_components.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public int indexOf(Component pc) {
|
||||||
|
return m_components.indexOf(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of children inside this container.
|
||||||
|
* @return the number of children inside this container.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return m_components.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Component get(int index) {
|
||||||
|
return (Component) m_components.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the components of this container.
|
||||||
|
* @return all the components of this container.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator children() {
|
||||||
|
return m_components.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XML tag that will be used to wrap the children of
|
||||||
|
* this container.
|
||||||
|
*
|
||||||
|
* @param tag the XML tag, or null if children will not be wrapped
|
||||||
|
* in any manner.
|
||||||
|
*/
|
||||||
|
protected final void setTag(String tag) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XML namespace for the tag that will be used to wrap
|
||||||
|
* the children of this container.
|
||||||
|
*
|
||||||
|
* @param ns the XML namespace
|
||||||
|
*/
|
||||||
|
protected final void setNamespace(String ns) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_ns = ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the name of the XML tag that will be used to
|
||||||
|
* wrap the child components.
|
||||||
|
*
|
||||||
|
* @return the name of the XML tag that will be used to
|
||||||
|
* wrap the child components, or null if no tag was specified.
|
||||||
|
*/
|
||||||
|
public final String getTag() {
|
||||||
|
return m_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the name of the XML namespace for the tag that will be used to
|
||||||
|
* wrap the child components.
|
||||||
|
*
|
||||||
|
* @return the name of the XML namespace for the tag that will be used to
|
||||||
|
* wrap the child components, or null if no namespace was specified.
|
||||||
|
*/
|
||||||
|
public final String getNamespace() {
|
||||||
|
return m_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the containing element. It is added with this
|
||||||
|
* component's tag below the specified parent element. If the passed in
|
||||||
|
* element is null, the method
|
||||||
|
* passes through p.
|
||||||
|
|
||||||
|
* @param p the parent XML element
|
||||||
|
* @return the element to which the children will be added.
|
||||||
|
*/
|
||||||
|
protected Element generateParent(Element p) {
|
||||||
|
String tag = getTag();
|
||||||
|
if (tag == null) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
Element parent = p.newChildElement(tag, getNamespace());
|
||||||
|
exportAttributes(parent);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML for this container. If the tag property
|
||||||
|
* is nonempty, wraps the children in the specified XML tag.
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
* @param p the parent XML element
|
||||||
|
* @see #setTag(String)
|
||||||
|
* @see #setNamespace(String)
|
||||||
|
*/
|
||||||
|
public void generateChildrenXML(PageState state, Element p) {
|
||||||
|
for (Iterator i = children(); i.hasNext(); ) {
|
||||||
|
Component c = (Component) i.next();
|
||||||
|
|
||||||
|
// XXX this seems to be a redundant vis check
|
||||||
|
if ( c.isVisible(state) ) {
|
||||||
|
c.generateXML(state, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML for this container. If the tag property
|
||||||
|
* is nonempty, wraps the children in the specified XML tag.
|
||||||
|
*
|
||||||
|
* @param state represents the current request
|
||||||
|
* @param p the parent XML element
|
||||||
|
* @see #setTag(String)
|
||||||
|
* @see #setNamespace(String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element p) {
|
||||||
|
if ( isVisible(state) ) {
|
||||||
|
Element parent = generateParent(p);
|
||||||
|
generateChildrenXML(state, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.ChangeListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates the selection of a single object from many
|
||||||
|
* possibilities. The <code>SingleSelectionModel</code> allows components to
|
||||||
|
* communicate selections without tying the component that manages the
|
||||||
|
* selection in the user interface (for example a {@link List}) to the
|
||||||
|
* components that consume the selection (such as an edit form that needs
|
||||||
|
* to know which object should be edited).
|
||||||
|
*
|
||||||
|
* <p> Selections are identified by a key, which must identify the
|
||||||
|
* underlying object uniquely among all objects that could possibly be
|
||||||
|
* selected. For objects stored in a database, this is usually a suitable
|
||||||
|
* representation of the object's primary key. The model relies on the
|
||||||
|
* key's <code>equals</code> method to compare keys, and requires that the
|
||||||
|
* key's <code>toString</code> method produces a representation of the key
|
||||||
|
* that can be used in URL strings and hidden form controls.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id: SingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
|
||||||
|
*/
|
||||||
|
public interface SingleSelectionModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if there is a selected element.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return <code>true</code> if there is a selected component;
|
||||||
|
* <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
boolean isSelected(PageState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key that identifies the selected element.
|
||||||
|
*
|
||||||
|
* @param state a <code>PageState</code> value
|
||||||
|
* @return a <code>String</code> value.
|
||||||
|
*/
|
||||||
|
Object getSelectedKey(PageState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selected key. If <code>key</code> is not in the collection of
|
||||||
|
* objects underlying this model, an
|
||||||
|
* <code>IllegalArgumentException</code> is thrown.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @param key the selected key
|
||||||
|
* @throws IllegalArgumentException if the supplied <code>key</code> can not
|
||||||
|
* be selected in the context of the current request.
|
||||||
|
*/
|
||||||
|
void setSelectedKey(PageState state, Object key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the selection.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @post ! isSelected(state)
|
||||||
|
*/
|
||||||
|
void clearSelection(PageState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a change listener to the model. The listener's
|
||||||
|
* <code>stateChanged</code> method is called whenever the selected key changes.
|
||||||
|
*
|
||||||
|
* @param l a listener to notify when the selected key changes
|
||||||
|
*/
|
||||||
|
void addChangeListener(ChangeListener l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a change listener from the model.
|
||||||
|
*
|
||||||
|
* @param l the listener to remove
|
||||||
|
*/
|
||||||
|
void removeChangeListener(ChangeListener l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the state parameter that will be used to keep track
|
||||||
|
* of the currently selected key. Typically, the implementing
|
||||||
|
* class will simply call:<br>
|
||||||
|
* <code><pre>return new StringParameter("foo");</pre></code><br>
|
||||||
|
* This method may return null if a state parameter is not
|
||||||
|
* appropriate in the context of the implementing class.
|
||||||
|
*
|
||||||
|
* @return the state parameter to use to keep
|
||||||
|
* track of the currently selected component, or
|
||||||
|
* null if a state parameter is not appropriate.
|
||||||
|
*/
|
||||||
|
ParameterModel getStateParameter();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component-defined event. Components usually fire an
|
||||||
|
* <code>ActionEvent</code> to indicate that they were the ones receiving
|
||||||
|
* the click in a user's submission, for example, a form might use an
|
||||||
|
* <code>ActionEvent</code> to signal that it has been submitted.
|
||||||
|
*
|
||||||
|
* @see ActionListener
|
||||||
|
* @see java.awt.event.ActionEvent
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ActionEvent extends PageEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an <code>ActionEvent</code>.
|
||||||
|
*
|
||||||
|
* @param source the component that originated the event
|
||||||
|
* @param state the state of the containing page under the current
|
||||||
|
* request
|
||||||
|
*/
|
||||||
|
public ActionEvent(Component source, PageState state) {
|
||||||
|
super(source, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener interface for receiving action events. The class that is
|
||||||
|
* interested in processing an action event implements this interface, and
|
||||||
|
* the object created with that class is registered with a component, using
|
||||||
|
* the component's addActionListener method. When the action event occurs,
|
||||||
|
* that object's actionPerformed method is invoked.
|
||||||
|
*
|
||||||
|
* @see ActionEvent
|
||||||
|
* @see java.awt.event.ActionListener
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface ActionListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an action has been performed.
|
||||||
|
*
|
||||||
|
* @pre e != null
|
||||||
|
*/
|
||||||
|
void actionPerformed(ActionEvent e);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will be
|
||||||
|
* renamed to SelectionEvent.
|
||||||
|
*/
|
||||||
|
public class ChangeEvent extends PageEvent {
|
||||||
|
|
||||||
|
public ChangeEvent(Object source, PageState state) {
|
||||||
|
super(source, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will be
|
||||||
|
* renamed to SelectionListener.
|
||||||
|
*/
|
||||||
|
public interface ChangeListener extends EventListener {
|
||||||
|
|
||||||
|
void stateChanged(ChangeEvent e);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience extensions to {@link javax.swing.event.EventListenerList
|
||||||
|
* Swing's <code>EventListenerList</code>}.
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class EventListenerList extends javax.swing.event.EventListenerList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append all the event listeners from <code>l</code>.
|
||||||
|
*
|
||||||
|
* @param l The list of listeners to copy from
|
||||||
|
*
|
||||||
|
* @pre l != null
|
||||||
|
*/
|
||||||
|
public void addAll(EventListenerList l) {
|
||||||
|
|
||||||
|
if ( l.listenerList.length == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object[] tmp = new Object[listenerList.length + l.listenerList.length];
|
||||||
|
System.arraycopy(listenerList, 0, tmp, 0, listenerList.length);
|
||||||
|
System.arraycopy(l.listenerList, 0,
|
||||||
|
tmp, listenerList.length, l.listenerList.length);
|
||||||
|
listenerList = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an iterator over all event listeners of class <code>t</code>.
|
||||||
|
* This iterator replaces the for loop mentioned in the documentation for
|
||||||
|
* {@link javax.swing.event.EventListenerList Swing's
|
||||||
|
* <code>EventListenerList</code>}.
|
||||||
|
*
|
||||||
|
* @param t The class of the event listeners that should be returned
|
||||||
|
*
|
||||||
|
* @pre t != null
|
||||||
|
* */
|
||||||
|
public Iterator getListenerIterator(final Class t) {
|
||||||
|
return new EventListenerIterator(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EventListenerIterator implements Iterator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener we will return with the next call to next().
|
||||||
|
* listener[_next] is always a class object of type t, unless all
|
||||||
|
* matching listeners have been returned, in which case _next
|
||||||
|
* is -1
|
||||||
|
* */
|
||||||
|
private int _count;
|
||||||
|
private int _next;
|
||||||
|
private Class _t;
|
||||||
|
|
||||||
|
EventListenerIterator(Class t) {
|
||||||
|
|
||||||
|
_count = getListenerList().length;
|
||||||
|
_next = -2;
|
||||||
|
_t = t;
|
||||||
|
findNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return (_next < _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object next() throws NoSuchElementException {
|
||||||
|
if ( ! hasNext() ) {
|
||||||
|
throw new NoSuchElementException("Iterator exhausted");
|
||||||
|
}
|
||||||
|
int result = _next;
|
||||||
|
findNext();
|
||||||
|
return getListenerList()[result+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException("Removal not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance <code>_next</code> so that either <code>_next == -1</code>
|
||||||
|
* if all listeners of class <code>_t</code> have been returned in the
|
||||||
|
* enclosing <code>EventListenerList</code>, or that
|
||||||
|
* <code>getListenersList()[_next] == _t</code> and
|
||||||
|
* <code>getListenersList()[_next+1]</code> (the corresponding listener
|
||||||
|
* object) has not been returned yet by <code>next()</code>.
|
||||||
|
* */
|
||||||
|
private void findNext() {
|
||||||
|
|
||||||
|
for (int i = _next+2; i<_count; i+=2) {
|
||||||
|
|
||||||
|
if (getListenerList()[i] == _t) {
|
||||||
|
_next = i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_next = _count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for a class that performs cleanup after
|
||||||
|
* cancelling out of a form
|
||||||
|
*
|
||||||
|
* @author Kevin Scaldeferri
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface FormCancelListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs any necessary cleanup after a user cancels out of
|
||||||
|
* a form
|
||||||
|
*
|
||||||
|
* <p>Implementations of this method are responsible for catching
|
||||||
|
* specific exceptions that may occur during processing, and either
|
||||||
|
* handling them internally or rethrowing them as instances of
|
||||||
|
* <code>FormProcessException</code> to be handled by the calling
|
||||||
|
* procedure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void cancel(FormSectionEvent e) throws FormProcessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for initializing a form with default values.
|
||||||
|
* Typical implementations of this interface query the database to
|
||||||
|
* set up an "edit" form, or obtain an id from a sequence to initialize
|
||||||
|
* a "create" form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface FormInitListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a FormData object already populated with values from
|
||||||
|
* the request.
|
||||||
|
*
|
||||||
|
* @param date The form data containing data included with this
|
||||||
|
* request. The initializer may require knowledge of form or
|
||||||
|
* parameter properties.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request associated with the
|
||||||
|
* initialization event. This supplied so that the initializer may
|
||||||
|
* rely on contextual information, such information extracted from
|
||||||
|
* headers or cookies or an associated <code>HttpSession</code>
|
||||||
|
* object.
|
||||||
|
* */
|
||||||
|
void init(FormSectionEvent e) throws FormProcessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for a class that performs a processing step
|
||||||
|
* on valid data.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface FormProcessListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a processing step on the data in the
|
||||||
|
* <code>FormData</code> object.
|
||||||
|
*
|
||||||
|
* <p>Implementations of this method are responsible for catching
|
||||||
|
* specific exceptions that may occur during processing, and either
|
||||||
|
* handling them internally or rethrowing them as instances of
|
||||||
|
* <code>FormProcessException</code> to be handled by the calling
|
||||||
|
* procedure.
|
||||||
|
*
|
||||||
|
* <p>Implementations of this method cannot assume success or
|
||||||
|
* failure of other FormProcessListeners associated with a
|
||||||
|
* particular FormModeel. Each implementation must act independently
|
||||||
|
*
|
||||||
|
* @param model The form model describing the structure and properties
|
||||||
|
* of the form data included with this request.
|
||||||
|
*
|
||||||
|
* @param data The container for all data objects associated with
|
||||||
|
* the request. String values for all parameters specified in the
|
||||||
|
* form model are converted to Java data objects and validated
|
||||||
|
* before processing occurs.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request information from which the form
|
||||||
|
* data was extracted. Note that the request object is supplied
|
||||||
|
* only in case the processing step requires contextual information
|
||||||
|
* (information extracted from cookies or the peer address, for
|
||||||
|
* example) or needs to modify session properties.
|
||||||
|
*
|
||||||
|
* @param response The HTTP response that will be returned to the
|
||||||
|
* user. The processing step may require access to this object to
|
||||||
|
* set cookies or handle errors. */
|
||||||
|
|
||||||
|
void process(FormSectionEvent e) throws FormProcessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event originating from a form. <code>FormSectionEvent</code>s are
|
||||||
|
* used to notify listeners that values in a form should be initilialized,
|
||||||
|
* validated or processed.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
* @see FormInitListener
|
||||||
|
* @see FormValidationListener
|
||||||
|
* @see FormProcessListener
|
||||||
|
*/
|
||||||
|
public class FormSectionEvent extends PageEvent {
|
||||||
|
|
||||||
|
private final transient FormData _formData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the form data for to the form that fired the event in the current
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* @return form data
|
||||||
|
*/
|
||||||
|
public final FormData getFormData() {
|
||||||
|
return _formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>FormSectionEvent</code>.
|
||||||
|
*
|
||||||
|
* @param source the form model that fired the event
|
||||||
|
* @param state the state of the enclosing page
|
||||||
|
* @param formData the form data constructed so far
|
||||||
|
*/
|
||||||
|
public FormSectionEvent(Object source,
|
||||||
|
PageState state,
|
||||||
|
FormData formData) {
|
||||||
|
super(source, state);
|
||||||
|
_formData = formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener called just before a form starts examining a
|
||||||
|
* submission. This listener can throw a {@link FormProcessException} to
|
||||||
|
* indicate that any further processing of the submission should be
|
||||||
|
* aborted. This usually leaves the corresponding {@link
|
||||||
|
* com.arsdigita.bebop.FormData FormData} object in an undefined
|
||||||
|
* state.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface FormSubmissionListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gets called as soon as the <code>FormData</code> for a
|
||||||
|
* form has been filled with the request parameters. The values in the
|
||||||
|
* <code>FormData</code> are transformed but not validated.
|
||||||
|
*
|
||||||
|
* @param e the event encapsulating form data, page state and event source
|
||||||
|
* @throws FormProcessException to signal that further processing of the
|
||||||
|
* form should be aborted.
|
||||||
|
*/
|
||||||
|
void submitted(FormSectionEvent e) throws FormProcessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for a class that implements a validation check
|
||||||
|
* on a set of form data.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface FormValidationListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a validation check on the specified <tt>FormData</tt>
|
||||||
|
* object, involving any number of parameters.
|
||||||
|
*
|
||||||
|
* <p>The check is always performed after all HTTP request
|
||||||
|
* parameters have been converted to data objects and stored in the
|
||||||
|
* FormData object.
|
||||||
|
*
|
||||||
|
* <p>If a validation error is encountered, the <tt>setError</tt>
|
||||||
|
* method of the <tt>FormData</tt> object may be used to set an
|
||||||
|
* error message for reporting back to the user.
|
||||||
|
*
|
||||||
|
* <p>This method is responsible for catching any exceptions that
|
||||||
|
* may occur during the validation. These exceptions may either
|
||||||
|
* be handled internally, or if they are unrecoverable may be
|
||||||
|
* rethrown as instances of <code>FormProcessException</code>.
|
||||||
|
*
|
||||||
|
* @param e FormSectionEvent containing the FormData as well as the
|
||||||
|
* PageState.
|
||||||
|
* Clients may access the PageState by executing something like
|
||||||
|
* PageState state = fse.getPageState();
|
||||||
|
* Method getFormData() allows access to the Form's data.
|
||||||
|
*
|
||||||
|
* @exception FormProcessException ff the data does not pass the check.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void validate(FormSectionEvent e) throws FormProcessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all page related events. All page related events
|
||||||
|
* should be derived from this class, since it defines a standard way to
|
||||||
|
* get at the source of the event and at the state of the page under the
|
||||||
|
* request that is currently being processed.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class PageEvent extends EventObject {
|
||||||
|
|
||||||
|
private transient PageState _state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new <code>PageEvent</code>.
|
||||||
|
* @param source the object firing the event, usually a {@link
|
||||||
|
* com.arsdigita.bebop.Component <code>Component</code>}.
|
||||||
|
* @param state the state of the page under the current request
|
||||||
|
*/
|
||||||
|
public PageEvent(Object source, PageState state) {
|
||||||
|
super(source);
|
||||||
|
_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the state of the page under the request in which the event was fired
|
||||||
|
*/
|
||||||
|
public final PageState getPageState() {
|
||||||
|
return _state;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event connected to a request parameter.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
* @see ParameterListener
|
||||||
|
* @see com.arsdigita.bebop.parameters.ParameterModel
|
||||||
|
* @see com.arsdigita.bebop.parameters.ParameterData
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ParameterEvent extends EventObject {
|
||||||
|
|
||||||
|
/* The request specific data about the event */
|
||||||
|
private transient ParameterData m_data;
|
||||||
|
private transient PageState m_state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>ParameterEvent</code>
|
||||||
|
*
|
||||||
|
* @param source the object that originated the event
|
||||||
|
* @param data the data for the parameter from the current request
|
||||||
|
**/
|
||||||
|
|
||||||
|
public ParameterEvent(Object source, ParameterData data) {
|
||||||
|
super(source);
|
||||||
|
m_data = data;
|
||||||
|
m_state = PageState.getPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the request specific data about the parameter.
|
||||||
|
**/
|
||||||
|
|
||||||
|
public final ParameterData getParameterData() {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
public PageState getPageState() {
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for a class that validates the values of a
|
||||||
|
* single parameter.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$ */
|
||||||
|
|
||||||
|
public interface ParameterListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a validation check on the data objects associated with a
|
||||||
|
* specific parameter. Validate should call
|
||||||
|
* ParameterData.addError() with a message regarding the nature
|
||||||
|
* of the error.
|
||||||
|
* @param e
|
||||||
|
* @throws com.arsdigita.bebop.FormProcessException
|
||||||
|
*/
|
||||||
|
void validate(ParameterEvent e) throws FormProcessException;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event originating from a component. <code>PrintEvent</code>s are
|
||||||
|
* fired just before the <code>source</code> component is output either as
|
||||||
|
* part of an XML document or as part of an HTML page.
|
||||||
|
*
|
||||||
|
* @see PrintListener
|
||||||
|
*
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PrintEvent extends PageEvent {
|
||||||
|
|
||||||
|
private Object m_target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a <code>PrintEvent</code>
|
||||||
|
*
|
||||||
|
* @param source the object that originated the event
|
||||||
|
* @param data the data for the parameter from the current request
|
||||||
|
* @pre source != null
|
||||||
|
* @pre target != null
|
||||||
|
*/
|
||||||
|
public PrintEvent(Object source, PageState state, Object target) {
|
||||||
|
super(source, state);
|
||||||
|
m_target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the target object, the one that can be freely modified by print
|
||||||
|
* listeners. Initially, the target is an unlocked clone of the source of
|
||||||
|
* the event.
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public final Object getTarget() {
|
||||||
|
return m_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners of this class are called just before a {@link com.arsdigita.bebop.Component} is
|
||||||
|
* about to be output, either in the form of an XML element, or by printing
|
||||||
|
* its HTML representation. The {@link #prepare prepare method} of the
|
||||||
|
* listener can make modifications to the {@link PrintEvent#getTarget
|
||||||
|
* target} of the event. The target will then be used to produce output
|
||||||
|
* instead of the source.
|
||||||
|
* <p>
|
||||||
|
* {@link PrintEvent PrintEvents} are <i>unicast</i> events, which means
|
||||||
|
* that components should only permit the registration of one
|
||||||
|
* <code>PrintListener</code>. Since the <code>PrintListener</code> is
|
||||||
|
* expected to modify the target, allowing multiple listeners to modify the
|
||||||
|
* target of one event would make it impossible to predict the resulting
|
||||||
|
* target component, since an individual listener can not know which
|
||||||
|
* listeners have run before it and which ones will run after it.
|
||||||
|
* <p>
|
||||||
|
* As an example consider the following code:
|
||||||
|
* <pre>
|
||||||
|
* Label l = new Label("Default text");
|
||||||
|
* l.addPrintListener( new PrintListener {
|
||||||
|
* private static final BigDecimal ONE = new BigDecimal(1);
|
||||||
|
* private BigDecimal count = new BigDecimal(0);
|
||||||
|
* public void prepare(PrintEvent e) {
|
||||||
|
* Label t = e.getTarget();
|
||||||
|
* synchronized (count) {
|
||||||
|
* count.add(ONE);
|
||||||
|
* }
|
||||||
|
* t.setLabel("Call no." + count + " since last server restart");
|
||||||
|
* }
|
||||||
|
* });</pre>
|
||||||
|
* Adding the label <code>l</code> to a page will lead to a label that
|
||||||
|
* changes in every request and print how many times the containing label
|
||||||
|
* has been called.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface PrintListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the target component returned by {@link PrintEvent#getTarget
|
||||||
|
* e.getTarget()} for output. The target component is an unlocked clone
|
||||||
|
* of the source of the event and can be freely modified within this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param e Event containing the page state, the source and the target of
|
||||||
|
* the event
|
||||||
|
*
|
||||||
|
* @see PrintEvent
|
||||||
|
*/
|
||||||
|
|
||||||
|
void prepare(PrintEvent e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event indicating that a Bebop page is being loaded, and control
|
||||||
|
* is about to be passed to the currently selected component
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
* @see ActionListener
|
||||||
|
* @see java.awt.event.ActionEvent
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RequestEvent extends PageEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an <code>ActionEvent</code>.
|
||||||
|
*
|
||||||
|
* @param source the component that originated the event
|
||||||
|
* @param state the state of the containing page under the current
|
||||||
|
* request
|
||||||
|
*/
|
||||||
|
public RequestEvent(Component source, PageState state) {
|
||||||
|
super(source, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener interface for receiving request events. The class that is
|
||||||
|
* interested in processing a request event implements this interface, and
|
||||||
|
* the object created with that class is registered with a Bebop page, using
|
||||||
|
* the Page.addRequestListener method. When the page has finished processing
|
||||||
|
* the page state, and is about to pass control to the currently selected
|
||||||
|
* component, the pageRequested method will be called.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
* @see ActionEvent
|
||||||
|
* @see java.awt.event.ActionListener
|
||||||
|
*/
|
||||||
|
public interface RequestListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an action has been performed.
|
||||||
|
*
|
||||||
|
* @pre e != null
|
||||||
|
*/
|
||||||
|
void pageRequested(RequestEvent e);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analogous to Widget
|
||||||
|
* PrintListeners, this is called when the widget is displayed (or
|
||||||
|
* validated) to get the dataset. The dataset should be created
|
||||||
|
* dynamically so it can vary according to form variables.
|
||||||
|
* Eventually, this may also support setting the initial value for a
|
||||||
|
* SearchAndSelect widget, so that it may act as an edit widget as
|
||||||
|
* well.
|
||||||
|
*
|
||||||
|
* @author Patrick McNeill
|
||||||
|
* @version $Id$
|
||||||
|
* @since 4.5 */
|
||||||
|
public interface SearchAndSelectListener extends EventListener {
|
||||||
|
|
||||||
|
SearchAndSelectModel getModel( PageEvent e );
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener interface for
|
||||||
|
* the SeachAndSelect Bebop widget. SearchAndSelect requires
|
||||||
|
* knowledge about the data it is searching over (to determine the
|
||||||
|
* display method and to actually execute the query).
|
||||||
|
*
|
||||||
|
* @author Patrick McNeill
|
||||||
|
* @version $Id$
|
||||||
|
* @since 4.5 */
|
||||||
|
public interface SearchAndSelectModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the user's search and restrict the result set to those queries
|
||||||
|
* that match. An empty string should return all results.
|
||||||
|
*
|
||||||
|
* @param query the user's search string, space or comma delimited words
|
||||||
|
*/
|
||||||
|
void setQuery ( String query );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the query that was last used.
|
||||||
|
*
|
||||||
|
* @return the query string
|
||||||
|
*/
|
||||||
|
String getQuery ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of items that are currently selected by the query
|
||||||
|
* string. If the query string is empty, this should return the number
|
||||||
|
* of items in the dataset.
|
||||||
|
*
|
||||||
|
* @return the number of currently selected items
|
||||||
|
*/
|
||||||
|
int resultsCount ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "i"th label (0 based indexing)
|
||||||
|
*
|
||||||
|
* @param i the label number to retrieve
|
||||||
|
* @return the ith label
|
||||||
|
*/
|
||||||
|
String getLabel (int i);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "i"th ID (0 based indexing)
|
||||||
|
*
|
||||||
|
* @param i the ID number to retrieve
|
||||||
|
* @return the ith ID
|
||||||
|
*/
|
||||||
|
String getID (int i);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implentation of the TableActionListener interface meant to save the
|
||||||
|
* developer from having to override both the {@link
|
||||||
|
* #cellSelected(TableActionEvent)} and {@link #headSelected(TableActionEvent)}
|
||||||
|
* methods when they only need to change the behavior of one.
|
||||||
|
*
|
||||||
|
* @see TableActionEvent
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class TableActionAdapter implements TableActionListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op implementation of {@link
|
||||||
|
* TableActionListener#cellSelected(TableActionEvent)}.
|
||||||
|
*
|
||||||
|
* @param e the event fired for the table.
|
||||||
|
*/
|
||||||
|
public void cellSelected(TableActionEvent e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op implementation of {@link
|
||||||
|
* TableActionListener#headSelected(TableActionEvent)}.
|
||||||
|
*
|
||||||
|
* @param e the event fired for the table.
|
||||||
|
*/
|
||||||
|
public void headSelected(TableActionEvent e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event for the {@link com.arsdigita.bebop.Table} component.
|
||||||
|
* Table will fire this event when one of its active cells receives a
|
||||||
|
* click.
|
||||||
|
*
|
||||||
|
* @see TableActionListener
|
||||||
|
* @see TableActionAdapter
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class TableActionEvent extends ActionEvent {
|
||||||
|
|
||||||
|
private Object m_rowKey;
|
||||||
|
private Integer m_column;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a TableActionEvent for a click on a particular row
|
||||||
|
* and a particular column.
|
||||||
|
*
|
||||||
|
* @param source the Component generating the event.
|
||||||
|
* @param s the state for the current request.
|
||||||
|
* @param rowKey the key for the row where the click was registered.
|
||||||
|
* @param column the index of the column where the click was registered.
|
||||||
|
*/
|
||||||
|
public TableActionEvent(Component source, PageState s,
|
||||||
|
Object rowKey, Integer column) {
|
||||||
|
super(source, s);
|
||||||
|
m_rowKey = rowKey;
|
||||||
|
m_column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a TableActionEvent for a click on a particular row.
|
||||||
|
*
|
||||||
|
* @param source the Component generating the event.
|
||||||
|
* @param s the state for the current request.
|
||||||
|
* @param rowKey the key for the row where the click was registered.
|
||||||
|
*/
|
||||||
|
public TableActionEvent(Component source, PageState s, Object rowKey) {
|
||||||
|
this(source, s, rowKey, new Integer(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key for the row that received the click.
|
||||||
|
*
|
||||||
|
* @return the key for the row that received the click.
|
||||||
|
*/
|
||||||
|
public final Object getRowKey() {
|
||||||
|
return m_rowKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index of the column that received the click.
|
||||||
|
*
|
||||||
|
* @return the index of the column that received the click.
|
||||||
|
*/
|
||||||
|
public final Integer getColumn() {
|
||||||
|
return m_column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the interface for handling events on {@link
|
||||||
|
* com.arsdigita.bebop.Table}. Programmers wishing to override just
|
||||||
|
* one of these methods, not both, may prefer to use {@link
|
||||||
|
* TableActionAdapter}.
|
||||||
|
*
|
||||||
|
* @see TableActionEvent
|
||||||
|
* @see TableActionAdapter
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface TableActionListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event handler for actions on a particular cell or a set of
|
||||||
|
* cells.
|
||||||
|
*
|
||||||
|
* @param e the event fired for the table.
|
||||||
|
*/
|
||||||
|
void cellSelected(TableActionEvent e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event handler for actions on a particular column heading or
|
||||||
|
* set of column headings.
|
||||||
|
*
|
||||||
|
* @param e the event fired for the table.
|
||||||
|
*/
|
||||||
|
void headSelected(TableActionEvent e);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event for the {@link com.arsdigita.bebop.Tree} component.
|
||||||
|
* Tree will fire this event when one of its nodes is expanded or
|
||||||
|
* collapsed.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class TreeExpansionEvent extends ActionEvent {
|
||||||
|
|
||||||
|
private Object m_nodeKey;
|
||||||
|
|
||||||
|
public TreeExpansionEvent(Component source, PageState s, Object nodeKey) {
|
||||||
|
super(source, s);
|
||||||
|
m_nodeKey = nodeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key for the node that was expanded or collapsed.
|
||||||
|
*
|
||||||
|
* @return the key for the node that was expanded or collapsed.
|
||||||
|
*/
|
||||||
|
public final Object getNodeKey() {
|
||||||
|
return m_nodeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener that is notified when a tree node is expanded or
|
||||||
|
* collapsed.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface TreeExpansionListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever an item in the tree has been collapsed.
|
||||||
|
*/
|
||||||
|
void treeCollapsed(TreeExpansionEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever an item in the tree has been expanded.
|
||||||
|
*/
|
||||||
|
void treeExpanded(TreeExpansionEvent event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ArrayParameter;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a <em>group</em> of associated checkboxes.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class CheckboxGroup extends OptionGroup implements BebopConstants {
|
||||||
|
|
||||||
|
public CheckboxGroup(String name) {
|
||||||
|
this(new ArrayParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckboxGroup(ArrayParameter param) {
|
||||||
|
super(param);
|
||||||
|
//m_xmlElement = BEBOP_CHECKBOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "checkbox";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The XML tag.
|
||||||
|
* @return The tag to be used for the top level DOM element
|
||||||
|
* generated for this type of Widget. */
|
||||||
|
@Override
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_CHECKBOXGROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOptionXMLElement() {
|
||||||
|
return BEBOP_CHECKBOX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2006 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Bebop;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays and manages a WYSIWYG HTML editor that takes advantage of DHTML scripting features. This
|
||||||
|
* class can use: - <a href="http://www.xinha.org>Xinha</a>
|
||||||
|
* - <a href="http://www.fckeditor.net">FCKeditor</a>
|
||||||
|
* - HTMLarea for backwards compatibility, development discontinued Editor is choosen based on the
|
||||||
|
* config parameter waf.bebop.dhtml_editor, default is "Xinha", which is the successor of HTMLarea
|
||||||
|
*
|
||||||
|
* @author Jim Parsons
|
||||||
|
* @author Richard Li
|
||||||
|
* @author Chris Burnett
|
||||||
|
* @author Alan Pevec
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DHTMLEditor extends TextArea {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>OFF</tt> value for the <tt>WRAP</tt> attribute of this image
|
||||||
|
* input.
|
||||||
|
*
|
||||||
|
* See <a href="http:
|
||||||
|
* //developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int OFF = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>HARD</tt> value for the <tt>WRAP</tt> attribute of this image
|
||||||
|
* input.
|
||||||
|
*
|
||||||
|
* See <a href="http://
|
||||||
|
* developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int HARD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>SOFT</tt> value for the <tt>WRAP</tt> attribute of this image
|
||||||
|
* input. See <a href="http://
|
||||||
|
* developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int SOFT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config objects for supported DHTMP editors
|
||||||
|
*/
|
||||||
|
public static class Config {
|
||||||
|
|
||||||
|
// WARNING: Processing of these default values by CMSConfig does NOT
|
||||||
|
// work correctly because of deviciencies in unmarshal method there.
|
||||||
|
public static final Config STANDARD = new Config("Xinha.Config",
|
||||||
|
"/assets/xinha/CCMcoreXinhaConfig.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example FCKEditor configuration.
|
||||||
|
*/
|
||||||
|
public static final Config FCK_STANDARD = new Config("FCKEditor.Config.StyleDefault",
|
||||||
|
"/assets/fckeditor/config/fckconfigstyledefault.js");
|
||||||
|
|
||||||
|
public static final Config FCK_CMSADMIN = new Config("FCKEditor.Config.StyleCMSAdmin",
|
||||||
|
"/assets/fckeditor/config/fckconfigstylecmsadmin.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example old HTMLarea configuration.
|
||||||
|
*/
|
||||||
|
public static final Config HTMLAREA = new Config("HTMLArea.Config", null);
|
||||||
|
|
||||||
|
private String m_name;
|
||||||
|
private String m_path;
|
||||||
|
|
||||||
|
public Config(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(String name,
|
||||||
|
String path) {
|
||||||
|
m_name = name;
|
||||||
|
m_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return m_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config valueOf(String cfg) {
|
||||||
|
int offset = cfg.indexOf(",");
|
||||||
|
if (offset != -1) {
|
||||||
|
return new Config(cfg.substring(0, offset),
|
||||||
|
cfg.substring(offset + 1));
|
||||||
|
} else {
|
||||||
|
return new Config(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (m_path == null) {
|
||||||
|
return m_name;
|
||||||
|
} else {
|
||||||
|
return m_name + "," + m_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end config object(s)
|
||||||
|
|
||||||
|
private Config m_config;
|
||||||
|
private Set m_plugins;
|
||||||
|
private Set m_hiddenButtons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
public DHTMLEditor(String name) {
|
||||||
|
this(new StringParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public DHTMLEditor(ParameterModel model) {
|
||||||
|
this(model, Config.STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
public DHTMLEditor(ParameterModel model,
|
||||||
|
Config config) {
|
||||||
|
super(model);
|
||||||
|
m_config = config;
|
||||||
|
m_plugins = new HashSet();
|
||||||
|
m_hiddenButtons = new HashSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "DHTMLEditor";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEditorURL() {
|
||||||
|
return Bebop.getConfig().getDHTMLEditorSrcFile().substring(
|
||||||
|
0, Bebop.getConfig().getDHTMLEditorSrcFile().lastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEditorSrc() {
|
||||||
|
return Bebop.getConfig().getDHTMLEditorSrcFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deprecated - use {@link setConfig(Config)}
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
public void setConfig(String config) {
|
||||||
|
setAttribute("config", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfig(Config config) {
|
||||||
|
m_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlugin(String name) {
|
||||||
|
m_plugins.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the specified button from being displayed in the editor toolbar.
|
||||||
|
*
|
||||||
|
* @param name name of the button, as specified in the btnList of the htmlarea.js file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void hideButton(String name) {
|
||||||
|
m_hiddenButtons.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ROWS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setRows(int rows) {
|
||||||
|
setAttribute("rows", String.valueOf(rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>COLS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setCols(int cols) {
|
||||||
|
setAttribute("cols", String.valueOf(cols));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>COLS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setWrap(int wrap) {
|
||||||
|
String wrapString = null;
|
||||||
|
|
||||||
|
switch (wrap) {
|
||||||
|
case OFF:
|
||||||
|
wrapString = "off";
|
||||||
|
break;
|
||||||
|
case HARD:
|
||||||
|
wrapString = "hard";
|
||||||
|
break;
|
||||||
|
case SOFT:
|
||||||
|
wrapString = "soft";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapString != null) {
|
||||||
|
setAttribute("wrap", wrapString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML tag.
|
||||||
|
*
|
||||||
|
* @return The tag to be used for the top level DOM element generated for this type of Widget.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getElementTag() {
|
||||||
|
return Bebop.getConfig().getDHTMLEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the DOM for the DHTML editor widget
|
||||||
|
* <p>
|
||||||
|
* Generates DOM fragment:
|
||||||
|
* <p>
|
||||||
|
* <code><bebop:dhtmleditor name=... value=... [onXXX=...]/>
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateWidget(PageState state, Element parent) {
|
||||||
|
String value = getParameterData(state).marshal();
|
||||||
|
Element editor = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
|
||||||
|
editor.addAttribute("name", getName());
|
||||||
|
generateDescriptionXML(state, editor);
|
||||||
|
|
||||||
|
// Set the needed config params so they don't have to be hardcoded in the theme
|
||||||
|
editor.addAttribute("editor_url", Web.getWebappContextPath().concat(getEditorURL()));
|
||||||
|
editor.addAttribute("editor_src", Web.getWebappContextPath().concat(getEditorSrc()));
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
editor.setText(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportAttributes(editor);
|
||||||
|
|
||||||
|
Element config = editor.newChildElement("bebop:config", BEBOP_XML_NS);
|
||||||
|
config.addAttribute("name", m_config.getName());
|
||||||
|
if (m_config.getPath() != null) {
|
||||||
|
config.addAttribute("path", Web.getWebappContextPath().concat(m_config.getPath()));
|
||||||
|
}
|
||||||
|
if (m_hiddenButtons.size() > 0) {
|
||||||
|
|
||||||
|
StringBuffer hiddenButtons = new StringBuffer();
|
||||||
|
// list must start and end with a space
|
||||||
|
hiddenButtons.append(" ");
|
||||||
|
Iterator hidden = m_hiddenButtons.iterator();
|
||||||
|
while (hidden.hasNext()) {
|
||||||
|
hiddenButtons.append(hidden.next());
|
||||||
|
hiddenButtons.append(" ");
|
||||||
|
}
|
||||||
|
config.addAttribute("hidden-buttons", hiddenButtons.toString());
|
||||||
|
}
|
||||||
|
Iterator plugins = m_plugins.iterator();
|
||||||
|
while (plugins.hasNext()) {
|
||||||
|
String name = (String) plugins.next();
|
||||||
|
Element plugin = editor.newChildElement("bebop:plugin", BEBOP_XML_NS);
|
||||||
|
plugin.addAttribute("name", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import java.text.DateFormatSymbols;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.DateParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.parameters.*;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
import com.arsdigita.globalization.GlobalizationHelper;
|
||||||
|
import com.arsdigita.bebop.util.GlobalizationUtil;
|
||||||
|
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a date field in an HTML form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Michael Pih
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Date extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
protected OptionGroup m_year;
|
||||||
|
protected OptionGroup m_month;
|
||||||
|
protected TextField m_day;
|
||||||
|
private int m_year_begin;
|
||||||
|
private int m_year_end;
|
||||||
|
private Locale m_locale;
|
||||||
|
private boolean yearAsc = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class for the year fragment
|
||||||
|
*/
|
||||||
|
protected class YearFragment extends SingleSelect {
|
||||||
|
|
||||||
|
protected Date parent;
|
||||||
|
private boolean autoCurrentYear; //Decide wether to set the current year if year is null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
public YearFragment(String name, Date parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
setHint(GlobalizationUtil.globalize("bebop.date.year.hint"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param autoCurrentYear
|
||||||
|
*/
|
||||||
|
public void setAutoCurrentYear(final boolean autoCurrentYear) {
|
||||||
|
this.autoCurrentYear = autoCurrentYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
ParameterModel model = parent.getParameterModel();
|
||||||
|
if (model instanceof IncompleteDateParameter) {
|
||||||
|
if (((IncompleteDateParameter) model).isYearSkipped()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object value = parent.getFragmentValue(ps, Calendar.YEAR);
|
||||||
|
if ((value == null) && autoCurrentYear) {
|
||||||
|
Calendar currentTime = GregorianCalendar.getInstance();
|
||||||
|
int currentYear = currentTime.get(Calendar.YEAR);
|
||||||
|
value = new Integer(currentYear);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected class MonthFragment extends SingleSelect {
|
||||||
|
|
||||||
|
protected Date parent;
|
||||||
|
|
||||||
|
public MonthFragment(String name, Date parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
ParameterModel model = parent.getParameterModel();
|
||||||
|
if (model instanceof IncompleteDateParameter) {
|
||||||
|
if (((IncompleteDateParameter) model).isMonthSkipped()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent.getFragmentValue(ps, Calendar.MONTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected class DayFragment extends TextField {
|
||||||
|
|
||||||
|
protected Date parent;
|
||||||
|
|
||||||
|
public DayFragment(String name, Date parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
ParameterModel model = parent.getParameterModel();
|
||||||
|
if (model instanceof IncompleteDateParameter) {
|
||||||
|
if (((IncompleteDateParameter) model).isDaySkipped()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent.getFragmentValue(ps, Calendar.DATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new Date. The model must be a DateParameter
|
||||||
|
*/
|
||||||
|
public Date(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
|
||||||
|
if (!(model instanceof DateParameter || model instanceof DateTimeParameter)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The Date widget " + model.getName()
|
||||||
|
+ " must be backed by a DateParameter parmeter model");
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = model.getName();
|
||||||
|
String nameYear = name + ".year";
|
||||||
|
String nameMonth = name + ".month";
|
||||||
|
String nameDay = name + ".day";
|
||||||
|
|
||||||
|
Calendar currentTime = GregorianCalendar.getInstance();
|
||||||
|
|
||||||
|
m_year = new YearFragment(nameYear, this);
|
||||||
|
m_month = new MonthFragment(nameMonth, this);
|
||||||
|
m_day = new DayFragment(nameDay, this);
|
||||||
|
|
||||||
|
m_day.setMaxLength(2);
|
||||||
|
m_day.setSize(2);
|
||||||
|
|
||||||
|
populateMonthOptions();
|
||||||
|
|
||||||
|
int currentYear = currentTime.get(Calendar.YEAR);
|
||||||
|
setYearRange(currentYear - 1, currentYear + 3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
public Date(String name) {
|
||||||
|
this(new DateParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoCurrentYear(final boolean autoCurrentYear) {
|
||||||
|
((YearFragment) m_year).setAutoCurrentYear(autoCurrentYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYearRange(int yearBegin, int yearEnd) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if (yearBegin != m_year_begin || yearEnd != m_year_end) {
|
||||||
|
m_year_begin = yearBegin;
|
||||||
|
m_year_end = yearEnd;
|
||||||
|
|
||||||
|
m_year.clearOptions();
|
||||||
|
if (this.getParameterModel() instanceof IncompleteDateParameter) {
|
||||||
|
// Create an empty year entry to unset a date, if either
|
||||||
|
// a) skipYearAllowed is true
|
||||||
|
// b) skipDayAllowed is true and skipMonthAllowed is true, to unset a date
|
||||||
|
if (((IncompleteDateParameter) this.getParameterModel()).isSkipYearAllowed()
|
||||||
|
|| (((IncompleteDateParameter) this.getParameterModel()).isSkipDayAllowed()
|
||||||
|
&& ((IncompleteDateParameter) this.getParameterModel())
|
||||||
|
.isSkipMonthAllowed())) {
|
||||||
|
m_year.addOption(new Option("", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yearAsc) {
|
||||||
|
for (int year = m_year_begin; year <= m_year_end; year++) {
|
||||||
|
m_year.addOption(new Option(String.valueOf(year)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(int year = m_year_end; year >= m_year_begin; year--) {
|
||||||
|
m_year.addOption(new Option(String.valueOf(year)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getYearAsc() {
|
||||||
|
return yearAsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYearAsc(final boolean yearAsc) {
|
||||||
|
this.yearAsc = yearAsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addYear(java.util.Date date) {
|
||||||
|
Calendar cal = new GregorianCalendar();
|
||||||
|
cal.setTime(date);
|
||||||
|
int year = (cal.get(Calendar.YEAR));
|
||||||
|
if (year < m_year_begin) {
|
||||||
|
m_year.prependOption(new Option(String.valueOf(year)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (year > m_year_end) {
|
||||||
|
m_year.addOption(new Option(String.valueOf(year)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>MAXLENGTH</tt> attribute for the <tt>INPUT</tt> tag used to render this form
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
*/
|
||||||
|
public void setMaxLength(int length) {
|
||||||
|
setAttribute("MAXLENGTH", String.valueOf(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompound() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML tag for this derived class of Widget.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateWidget(PageState ps, Element parent) {
|
||||||
|
|
||||||
|
if (!isVisible(ps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element date = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
date.addAttribute("name", getParameterModel().getName());
|
||||||
|
if (getLabel() != null) {
|
||||||
|
date.addAttribute("label", (String) getLabel().localize(ps.getRequest()));
|
||||||
|
}
|
||||||
|
exportAttributes(date);
|
||||||
|
generateDescriptionXML(ps, date);
|
||||||
|
generateLocalizedWidget(ps, date);
|
||||||
|
|
||||||
|
// If Element could be null insert an extra widget to clear entry
|
||||||
|
if (!hasValidationListener(new NotNullValidationListener())) {
|
||||||
|
date.newChildElement("NoDate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resepct the localized
|
||||||
|
public void generateLocalizedWidget(PageState ps, Element date) {
|
||||||
|
|
||||||
|
Locale defaultLocale = Locale.getDefault();
|
||||||
|
Locale locale = GlobalizationHelper.getNegotiatedLocale();
|
||||||
|
|
||||||
|
// Get the current Pattern
|
||||||
|
// XXX This is really, really, really, really, really, really bad
|
||||||
|
// but there is no way to get a SimpleDateFormat object for a
|
||||||
|
// different locale the the system default (the one you get with
|
||||||
|
// Locale.getDefault();). Also there is now way getting the pattern
|
||||||
|
// in another way (up until JDK 1.1 there was), so I have to temporarly
|
||||||
|
// switch the default locale to my desired locale, get a SimpleDateFormat
|
||||||
|
// and switch back.
|
||||||
|
Locale.setDefault(locale);
|
||||||
|
String format = new SimpleDateFormat().toPattern();
|
||||||
|
Locale.setDefault(defaultLocale);
|
||||||
|
|
||||||
|
// Repopulate the options for the month select box to get them localized
|
||||||
|
populateMonthOptions();
|
||||||
|
|
||||||
|
char[] chars = format.toCharArray();
|
||||||
|
for (int i = 0; i < chars.length; i++) {
|
||||||
|
|
||||||
|
// Test for doublettes
|
||||||
|
if (i >= 1 && chars[i - 1] == chars[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (chars[i]) {
|
||||||
|
case 'd':
|
||||||
|
m_day.generateXML(ps, date);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
m_month.generateXML(ps, date);
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
m_year.generateXML(ps, date);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisabled() {
|
||||||
|
m_month.setDisabled();
|
||||||
|
m_day.setDisabled();
|
||||||
|
m_year.setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly() {
|
||||||
|
m_month.setReadOnly();
|
||||||
|
m_day.setReadOnly();
|
||||||
|
m_year.setReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Form Object for this Widget. This method will throw an exception if the _form
|
||||||
|
* pointer is already set. To explicity change the _form pointer the developer must first call
|
||||||
|
* setForm(null)
|
||||||
|
*
|
||||||
|
* @param f the <code>Form</code> Object for this Widget.
|
||||||
|
*
|
||||||
|
* @exception IllegalStateException if form already set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setForm(Form f) {
|
||||||
|
super.setForm(f);
|
||||||
|
m_year.setForm(f);
|
||||||
|
m_month.setForm(f);
|
||||||
|
m_day.setForm(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getFragmentValue(PageState ps, int field) {
|
||||||
|
Assert.exists(ps, "PageState");
|
||||||
|
FormData f = getForm().getFormData(ps);
|
||||||
|
if (f != null) {
|
||||||
|
java.util.Date value = (java.util.Date) f.get(getName());
|
||||||
|
if (value != null) {
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
c.setTime(value);
|
||||||
|
return new Integer(c.get(field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClassAttr(String at) {
|
||||||
|
m_month.setClassAttr(at);
|
||||||
|
m_year.setClassAttr(at);
|
||||||
|
m_day.setClassAttr(at);
|
||||||
|
super.setClassAttr(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateMonthOptions() {
|
||||||
|
|
||||||
|
Locale locale = GlobalizationHelper.getNegotiatedLocale();
|
||||||
|
|
||||||
|
if (m_locale == null || (locale != null && !m_locale.equals(locale))) {
|
||||||
|
|
||||||
|
DateFormatSymbols dfs = new DateFormatSymbols(locale);
|
||||||
|
String[] months = dfs.getMonths();
|
||||||
|
|
||||||
|
m_month.clearOptions();
|
||||||
|
|
||||||
|
if (this.getParameterModel() instanceof IncompleteDateParameter) {
|
||||||
|
if (((IncompleteDateParameter) this.getParameterModel()).isSkipMonthAllowed()) {
|
||||||
|
m_month.addOption(new Option("", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < months.length; i += 1) {
|
||||||
|
// This check is necessary because
|
||||||
|
// java.text.DateFormatSymbols.getMonths() returns an array
|
||||||
|
// of 13 Strings: 12 month names and an empty string.
|
||||||
|
if (months[i].length() > 0) {
|
||||||
|
m_month.addOption(new Option(String.valueOf(i), months[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_locale = GlobalizationHelper.getNegotiatedLocale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.DateTimeParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.NotNullValidationListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a date and time field in an HTML form.
|
||||||
|
* (based on the code in Date.java)
|
||||||
|
*
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DateTime extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
private Date m_date;
|
||||||
|
private Time m_time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new DateTime. The model must be a DateTimeParameter
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public DateTime(ParameterModel model) {
|
||||||
|
this(model, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new DateTime. The model must be a DateTimeParameter
|
||||||
|
* @param model
|
||||||
|
* @param showSeconds
|
||||||
|
*/
|
||||||
|
public DateTime(ParameterModel model, boolean showSeconds) {
|
||||||
|
super(model);
|
||||||
|
|
||||||
|
if (!(model instanceof DateTimeParameter)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The DateTime widget " + model.getName() +
|
||||||
|
" must be backed by a DateTimeParameter parmeter model");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_date = new Date(model);
|
||||||
|
m_time = new Time(model, showSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime(String name) {
|
||||||
|
this(new DateTimeParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYearRange(int startYear, int endYear) {
|
||||||
|
m_date.setYearRange(startYear, endYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "dateTime";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>MAXLENGTH</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setMaxLength(int length) {
|
||||||
|
setAttribute("MAXLENGTH", String.valueOf(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The XML tag for this derived class of Widget.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_DATETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateWidget(PageState ps, Element parent) {
|
||||||
|
|
||||||
|
if (!isVisible(ps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element datetime = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
datetime.addAttribute("name", getParameterModel().getName());
|
||||||
|
m_date.generateLocalizedWidget(ps, datetime);
|
||||||
|
m_time.generateLocalizedWidget(ps, datetime);
|
||||||
|
|
||||||
|
generateDescriptionXML(ps, datetime);
|
||||||
|
|
||||||
|
// If Element could be null insert a extra widget to clear entry
|
||||||
|
if (!hasValidationListener(new NotNullValidationListener())) {
|
||||||
|
datetime.newChildElement("NoDateTime");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisabled() {
|
||||||
|
m_date.setDisabled();
|
||||||
|
m_time.setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly() {
|
||||||
|
m_date.setReadOnly();
|
||||||
|
m_time.setReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Form Object for this Widget. This method will throw an
|
||||||
|
* exception if the _form pointer is already set. To explicity
|
||||||
|
* change the _form pointer the developer must first call
|
||||||
|
* setForm(null)
|
||||||
|
*
|
||||||
|
* @param the <code>Form</code> Object for this Widget.
|
||||||
|
* @exception IllegalStateException if form already set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setForm(Form f) {
|
||||||
|
super.setForm(f);
|
||||||
|
m_date.setForm(f);
|
||||||
|
m_time.setForm(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.form.Widget;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a textarea field in an HTML form.
|
||||||
|
*
|
||||||
|
* @deprecated See {@link DHTMLEditor}
|
||||||
|
* @author Jim Parsons
|
||||||
|
*/
|
||||||
|
public class Deditor extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>OFF</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. See <a
|
||||||
|
* href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does. */
|
||||||
|
|
||||||
|
public static final int OFF = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>HARD</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. * See <a
|
||||||
|
* //href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int HARD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>SOFT</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. See <a
|
||||||
|
* href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int SOFT = 2;
|
||||||
|
|
||||||
|
public Deditor(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Deditor(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "deditor";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default value (text)
|
||||||
|
* @deprecated [since 17Aug2001] use {@link Widget#setDefaultValue(Object)}
|
||||||
|
*/
|
||||||
|
public void setValue( String text ) {
|
||||||
|
this.setDefaultValue(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ROWS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setRows(int rows) {
|
||||||
|
setAttribute("rows", String.valueOf(rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>COLS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setCols(int cols) {
|
||||||
|
setAttribute("cols", String.valueOf(cols));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>COLS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setWrap(int wrap) {
|
||||||
|
String wrapString = null;
|
||||||
|
|
||||||
|
switch (wrap) {
|
||||||
|
case OFF:
|
||||||
|
wrapString = "off";
|
||||||
|
break;
|
||||||
|
case HARD:
|
||||||
|
wrapString = "hard";
|
||||||
|
break;
|
||||||
|
case SOFT:
|
||||||
|
wrapString = "soft";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapString != null) {
|
||||||
|
setAttribute("wrap", wrapString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a compound widget?
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The XML tag.
|
||||||
|
* @return The tag to be used for the top level DOM element
|
||||||
|
* generated for this type of Widget. */
|
||||||
|
protected String getElementTag() {
|
||||||
|
return "bebop:deditor";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the DOM for the textarea widget
|
||||||
|
* <p>Generates DOM fragment:
|
||||||
|
* <p><code><bebop:textarea name=... value=... [onXXX=...]/>
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
public void generateWidget( PageState state, Element parent ) {
|
||||||
|
|
||||||
|
Element deditor = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
|
||||||
|
deditor.addAttribute("name", getName());
|
||||||
|
|
||||||
|
String userAgent =
|
||||||
|
state.getRequest().getHeader("user-agent").toLowerCase();
|
||||||
|
boolean isIE55 =
|
||||||
|
(userAgent != null &&
|
||||||
|
((userAgent.indexOf("msie 5.5") != -1) ||
|
||||||
|
(userAgent.indexOf("msie 6") != -1)));
|
||||||
|
|
||||||
|
deditor.addAttribute("isIE55", (new Boolean(isIE55)).toString());
|
||||||
|
|
||||||
|
|
||||||
|
String value = getParameterData(state).marshal();
|
||||||
|
if ( value == null ) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
Element texter = deditor.newChildElement("bebop:textcontent",BEBOP_XML_NS);
|
||||||
|
texter.setCDATASection(value);
|
||||||
|
exportAttributes(deditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fieldset for form.
|
||||||
|
*
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
*/
|
||||||
|
public class Fieldset extends SimpleContainer {
|
||||||
|
|
||||||
|
GlobalizedMessage m_title;
|
||||||
|
|
||||||
|
public Fieldset(GlobalizedMessage title) {
|
||||||
|
super("bebop:fieldset", BEBOP_XML_NS);
|
||||||
|
m_title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element p) {
|
||||||
|
if (isVisible(state)) {
|
||||||
|
Element parent = generateParent(p);
|
||||||
|
parent.addAttribute("legend", (String) m_title.localize());
|
||||||
|
generateChildrenXML(state, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.ParameterEvent;
|
||||||
|
import com.arsdigita.bebop.parameters.GlobalizedParameterListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.dispatcher.MultipartHttpServletRequest;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a file upload widget.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$ */
|
||||||
|
|
||||||
|
public class FileUpload extends Widget {
|
||||||
|
|
||||||
|
public FileUpload(String name) {
|
||||||
|
this(name, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileUpload(String name, boolean validateInputFile) {
|
||||||
|
super(name);
|
||||||
|
addValidationListener(new FileExistsValidationListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileUpload(ParameterModel model) {
|
||||||
|
this(model, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileUpload(ParameterModel model, boolean validateInputFile) {
|
||||||
|
super(model);
|
||||||
|
addValidationListener(new FileExistsValidationListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "file";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private class FileExistsValidationListener extends GlobalizedParameterListener {
|
||||||
|
|
||||||
|
public FileExistsValidationListener() {
|
||||||
|
setError(new GlobalizedMessage("file_empty_or_not_found", getBundleBaseName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate (ParameterEvent e) {
|
||||||
|
ParameterData data = e.getParameterData();
|
||||||
|
HttpServletRequest request = e.getPageState().getRequest();
|
||||||
|
String filename = (String) data.getValue();
|
||||||
|
|
||||||
|
if (!(request instanceof MultipartHttpServletRequest) ||
|
||||||
|
filename == null ||
|
||||||
|
filename.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((MultipartHttpServletRequest) request).getFile(data.getModel()
|
||||||
|
.getName())
|
||||||
|
.length()==0) {
|
||||||
|
data.addError(filename + " " + getError().localize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.PageErrorDisplay;
|
||||||
|
import com.arsdigita.bebop.list.ListModelBuilder;
|
||||||
|
import com.arsdigita.bebop.list.ListModel;
|
||||||
|
import com.arsdigita.bebop.List;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.util.LockableImpl;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays validation errors on the form which were added by the form's
|
||||||
|
* validation listener. Does not handle errors in the individual parameters;
|
||||||
|
* these errors are handled by the form's template. This class is not
|
||||||
|
* a form widget, since it does not produce a value.
|
||||||
|
*
|
||||||
|
* @author Stanislav Freidin
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FormErrorDisplay extends PageErrorDisplay {
|
||||||
|
|
||||||
|
private Form m_form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new <code>FormErrorDisplay</code>
|
||||||
|
*
|
||||||
|
* @param form The parent form whose errors will be displayed by
|
||||||
|
* this widget
|
||||||
|
*/
|
||||||
|
public FormErrorDisplay(Form form) {
|
||||||
|
super(new FormErrorModelBuilder(form));
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form whose errors are to be displayed
|
||||||
|
* @return the form whose errors are to be displayed
|
||||||
|
*/
|
||||||
|
public final Form getForm() {
|
||||||
|
return m_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if there are errors to display
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @return true if there are any errors to display; false otherwise
|
||||||
|
*/
|
||||||
|
protected boolean hasErrors(PageState state) {
|
||||||
|
FormData data = m_form.getFormData(state);
|
||||||
|
return (data != null && data.getErrors().hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A private class which builds a ListModel based on form errors
|
||||||
|
private static class FormErrorModelBuilder extends LockableImpl
|
||||||
|
implements ListModelBuilder {
|
||||||
|
|
||||||
|
private Form m_form;
|
||||||
|
|
||||||
|
public FormErrorModelBuilder(Form form) {
|
||||||
|
super();
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListModel makeModel(List l, PageState state) {
|
||||||
|
FormData data = m_form.getFormData(state);
|
||||||
|
if(data == null) {
|
||||||
|
return new StringIteratorModel(Collections.EMPTY_LIST.iterator());
|
||||||
|
} else {
|
||||||
|
return new StringIteratorModel(data.getErrors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a hidden HTML form element.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Hidden extends Widget {
|
||||||
|
|
||||||
|
public Hidden(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hidden(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.util.PanelConstraints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing an image HTML form element.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ImageSubmit extends Widget implements PanelConstraints {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
public ImageSubmit(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageSubmit(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "image";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>SRC</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*
|
||||||
|
* @param location
|
||||||
|
*/
|
||||||
|
public void setSrc(String location) {
|
||||||
|
setAttribute("src",location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the <tt>ALRT</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setAlt(String alt) {
|
||||||
|
setAttribute("alt",alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ALIGN</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element. Uses the positional constants defined
|
||||||
|
* in Interface PanelConstraints.
|
||||||
|
* Note: These may be refactored in future versions.
|
||||||
|
*
|
||||||
|
* @param align Symbolic constant denoting the alignment.
|
||||||
|
*/
|
||||||
|
public void setAlign(int align) {
|
||||||
|
String alignString = null;
|
||||||
|
|
||||||
|
switch (align) {
|
||||||
|
case LEFT:
|
||||||
|
alignString = "left";
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
alignString = "right";
|
||||||
|
break;
|
||||||
|
case TOP:
|
||||||
|
alignString = "top";
|
||||||
|
break;
|
||||||
|
case ABSMIDDLE:
|
||||||
|
alignString = "absmiddle";
|
||||||
|
break;
|
||||||
|
case ABSBOTTOM:
|
||||||
|
alignString = "absbottom";
|
||||||
|
break;
|
||||||
|
case TEXTTOP:
|
||||||
|
alignString = "texttop";
|
||||||
|
break;
|
||||||
|
case MIDDLE:
|
||||||
|
alignString = "middle";
|
||||||
|
break;
|
||||||
|
case BASELINE:
|
||||||
|
alignString = "baseline";
|
||||||
|
break;
|
||||||
|
case BOTTOM:
|
||||||
|
alignString = "botton";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alignString != null)
|
||||||
|
setAttribute("align",alignString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method for rendering this Image widget in a visitor.
|
||||||
|
*/
|
||||||
|
/* public void accept(FormVisitor visitor) throws IOException {
|
||||||
|
visitor.visitImage(this);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ArrayParameter;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class
|
||||||
|
* representing an HTML <code>SELECT</code> element.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$ */
|
||||||
|
public class MultipleSelect extends Select implements BebopConstants {
|
||||||
|
|
||||||
|
public MultipleSelect(String name) {
|
||||||
|
super(new ArrayParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** State that this is a multiple select
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public boolean isMultiple()
|
||||||
|
{ return true; }
|
||||||
|
|
||||||
|
/** The XML tag for this derived class of Widget. */
|
||||||
|
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_MULTISELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,561 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormStep;
|
||||||
|
import com.arsdigita.bebop.GridPanel;
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.RequestLocal;
|
||||||
|
import com.arsdigita.bebop.event.FormInitListener;
|
||||||
|
import com.arsdigita.bebop.event.FormProcessListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.parameters.ArrayParameter;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Multiple select widget pair for knowledge types. This FormStep
|
||||||
|
* displays two multiple select widgets, one which contains possible
|
||||||
|
* the user may want to add, and the right displays the options that
|
||||||
|
* are currently applicable. </p>
|
||||||
|
*
|
||||||
|
* <p>To use the widget, you should call {@link
|
||||||
|
* #setLeftMultipleSelect(RequestLocal)} and {@link
|
||||||
|
* #setRightMultipleSelect(RequestLocal)} and pass in the appropriate
|
||||||
|
* collections to initialize the MutlipleSelect options. Then, in the
|
||||||
|
* process listener of the form in which the MultipleSelectPairWidget
|
||||||
|
* is embedded, call {@link #getSelectedOptions(PageState)} and {@link
|
||||||
|
* #getUnselectedOptions(PageState)} to get the chosen values. The process
|
||||||
|
* listener for the parent form must use the Submit.isSelected(ps) so
|
||||||
|
* that the process listener can distinguish between different types
|
||||||
|
* of form submits.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that the right multiple select can be empty and does not need
|
||||||
|
* to be set. This class also uses a relatively inefficient
|
||||||
|
* implementation of removeOption in {@link OptionGroup OptionGroup}
|
||||||
|
* so that operations run in O(N^2). This can be reduced to O(N) with
|
||||||
|
* a more optimal implementation of OptionGroup.</p>
|
||||||
|
*
|
||||||
|
* @see Option
|
||||||
|
* @see OptionGroup
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class MultipleSelectPairWidget extends FormStep {
|
||||||
|
|
||||||
|
private static final Logger s_log =
|
||||||
|
Logger.getLogger(MultipleSelectPairWidget.class);
|
||||||
|
|
||||||
|
private Hidden m_addSelectOptions;
|
||||||
|
private Hidden m_removeSelectOptions;
|
||||||
|
private MultipleSelect m_addSelect;
|
||||||
|
private MultipleSelect m_removeSelect;
|
||||||
|
private Submit m_addSubmit;
|
||||||
|
private Submit m_removeSubmit;
|
||||||
|
private RequestLocal m_addSelectDataSource;
|
||||||
|
private RequestLocal m_removeSelectDataSource;
|
||||||
|
private RequestLocal m_selectsPopulated;
|
||||||
|
private RequestLocal m_leftSelectMap = null;
|
||||||
|
private RequestLocal m_rightSelectMap = null;
|
||||||
|
private boolean m_leftSideChanges;
|
||||||
|
|
||||||
|
private String m_qualifier;
|
||||||
|
|
||||||
|
private final static int RIGHT = 1;
|
||||||
|
private final static int LEFT = 2;
|
||||||
|
|
||||||
|
// Empty array for internal use. Should be part of a generic utility class.
|
||||||
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
|
|
||||||
|
// Configuration options.
|
||||||
|
private int m_multipleSelectSize = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This create a standard MultipleSelectPairWidget with the
|
||||||
|
* default names used for internal widgets.
|
||||||
|
*/
|
||||||
|
public MultipleSelectPairWidget() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipleSelectPairWidget(String nameQualifier) {
|
||||||
|
super(nameQualifier + "MultipleSelectPairWidget", new GridPanel(3));
|
||||||
|
m_qualifier = nameQualifier;
|
||||||
|
|
||||||
|
m_addSelectOptions =
|
||||||
|
new Hidden(new ArrayParameter(qualify("addSelectOptions")));
|
||||||
|
m_removeSelectOptions = new Hidden
|
||||||
|
(new ArrayParameter(qualify("removeSelectOptions")));
|
||||||
|
|
||||||
|
m_addSelect = new MultipleSelect(qualify("leftSelect"));
|
||||||
|
m_addSelect.setSize(m_multipleSelectSize);
|
||||||
|
m_removeSelect = new MultipleSelect(qualify("rightSelect"));
|
||||||
|
m_removeSelect.setSize(m_multipleSelectSize);
|
||||||
|
setLeftSideChanges(true);
|
||||||
|
|
||||||
|
m_addSubmit = new Submit(qualify("->"), " -> ");
|
||||||
|
m_removeSubmit = new Submit(qualify("<-"), " <- ");
|
||||||
|
|
||||||
|
GridPanel centerPanel = new GridPanel(1);
|
||||||
|
centerPanel.add(m_addSubmit);
|
||||||
|
centerPanel.add(m_removeSubmit);
|
||||||
|
|
||||||
|
add(m_addSelect, GridPanel.LEFT);
|
||||||
|
add(centerPanel, GridPanel.CENTER);
|
||||||
|
add(m_removeSelect, GridPanel.RIGHT);
|
||||||
|
add(m_addSelectOptions);
|
||||||
|
add(m_removeSelectOptions);
|
||||||
|
|
||||||
|
m_selectsPopulated = new RequestLocal();
|
||||||
|
addInitListener(new MultipleSelectPairFormInitListener
|
||||||
|
(m_selectsPopulated));
|
||||||
|
addProcessListener(new MultipleSelectPairFormProcessListener
|
||||||
|
(m_selectsPopulated));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelected(PageState ps) {
|
||||||
|
return m_addSubmit.isSelected(ps) || m_removeSubmit.isSelected(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param collection A collection of Option objects
|
||||||
|
*/
|
||||||
|
public void setLeftMultipleSelect(RequestLocal collection) {
|
||||||
|
m_addSelectDataSource = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lets the user pass in a RequestLocal that returns
|
||||||
|
* a java.util.Map that contains the option value as the
|
||||||
|
* key and the actual option as the map value.
|
||||||
|
* When populating the left select, the system will use
|
||||||
|
* this map before falling back to the default map.
|
||||||
|
*/
|
||||||
|
public void setLeftMultipleSelectMap(RequestLocal map) {
|
||||||
|
m_leftSelectMap = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lets the user pass in a RequestLocal that returns
|
||||||
|
* a java.util.Map that contains the option value as the
|
||||||
|
* key and the actual option as the map value.
|
||||||
|
* When populating the left select, the system will use
|
||||||
|
* this map before falling back to the default map.
|
||||||
|
*/
|
||||||
|
public void setRightMultipleSelectMap(RequestLocal map) {
|
||||||
|
m_rightSelectMap = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the left select widget so that callers
|
||||||
|
* can have access to the underlying parameters and
|
||||||
|
* other features (e.g. in case they need to add a
|
||||||
|
* ParameterListener)
|
||||||
|
*/
|
||||||
|
public Widget getLeftSelect() {
|
||||||
|
return m_addSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the left select widget so that callers
|
||||||
|
* can have access to the underlying parameters and
|
||||||
|
* other features (e.g. in case they need to add a
|
||||||
|
* ParameterListener)
|
||||||
|
*/
|
||||||
|
public Widget getRightSelect() {
|
||||||
|
return m_removeSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param doesChange This indicates whether the items in the
|
||||||
|
* "to add" select box are removed as they are added to
|
||||||
|
* the "to remove" select box. That is, as choices are
|
||||||
|
* selected, should they be removed from the list of
|
||||||
|
* choices? This defaults to true.
|
||||||
|
*/
|
||||||
|
public void setLeftSideChanges(boolean doesChange) {
|
||||||
|
m_leftSideChanges = doesChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns an indication of whether or not the left
|
||||||
|
* multiple select changes as items are moved from the left
|
||||||
|
* to the right.
|
||||||
|
*/
|
||||||
|
public boolean leftSideChanges() {
|
||||||
|
return m_leftSideChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param collection A collection of Option objects
|
||||||
|
*/
|
||||||
|
public void setRightMultipleSelect(RequestLocal collection) {
|
||||||
|
m_removeSelectDataSource = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the selected options, those selected from the left hand widget
|
||||||
|
*
|
||||||
|
* @return array of options
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public String[] getSelectedOptions(PageState ps) {
|
||||||
|
String[] options = (String[]) m_removeSelectOptions.getValue(ps);
|
||||||
|
// Probably unneccessary, as widget should be populated with EMPTY_STRING_ARRAY in init listener
|
||||||
|
// if there is no data.
|
||||||
|
if (null == options) {
|
||||||
|
options = EMPTY_STRING_ARRAY;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unselected options, those removed from the right hand widget
|
||||||
|
*
|
||||||
|
* @return array of options
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public String[] getUnselectedOptions(PageState ps) {
|
||||||
|
String[] options = (String[]) m_addSelectOptions.getValue(ps);
|
||||||
|
// Probably unneccessary, as widget should be populated with EMPTY_STRING_ARRAY in init listener
|
||||||
|
// if there is no data.
|
||||||
|
if (null == options) {
|
||||||
|
options = EMPTY_STRING_ARRAY;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateXML(PageState state, Element element) {
|
||||||
|
// if the page has not been populated then it need to
|
||||||
|
// be populated from the hidden variables. Otherwise,
|
||||||
|
// nothing will be displayed in the multi-select boxes.
|
||||||
|
if (!Boolean.TRUE.equals(m_selectsPopulated.get(state)) &&
|
||||||
|
isInitialized(state)) {
|
||||||
|
List addOptions = new ArrayList();
|
||||||
|
List removeOptions = new ArrayList();
|
||||||
|
|
||||||
|
String[] unselected = getUnselectedOptions(state);
|
||||||
|
for (int i = 0; i < unselected.length; i++) {
|
||||||
|
String option = unselected[i];
|
||||||
|
addOptions.add(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] selected = getSelectedOptions(state);
|
||||||
|
for (int i = 0; i < selected.length; i++) {
|
||||||
|
String option = selected[i];
|
||||||
|
removeOptions.add(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
m_selectsPopulated.set(state, Boolean.TRUE);
|
||||||
|
generateOptionValues(state, addOptions, removeOptions,
|
||||||
|
setupOptionMap(state));
|
||||||
|
m_addSelect.addOption(getEmptyOption(), state);
|
||||||
|
m_removeSelect.addOption(getEmptyOption(), state);
|
||||||
|
}
|
||||||
|
super.generateXML(state, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This changes the name of the parameter so that it is possible
|
||||||
|
* to include several of these on the same page.
|
||||||
|
*/
|
||||||
|
private String qualify(String property) {
|
||||||
|
if (m_qualifier != null) {
|
||||||
|
return m_qualifier + "_" + property;
|
||||||
|
} else {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @size The number of rows to display in the multiple selects.
|
||||||
|
*/
|
||||||
|
public void setMultipleSelectSize(int size) {
|
||||||
|
m_multipleSelectSize = size;
|
||||||
|
m_removeSelect.setSize(m_multipleSelectSize);
|
||||||
|
m_addSelect.setSize(m_multipleSelectSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap setupOptionMap(PageState ps) {
|
||||||
|
// We put all of our options into a HashMap so that we can add the
|
||||||
|
// Option object to the destination MultipleSelect.
|
||||||
|
HashMap optionsMap = new HashMap();
|
||||||
|
Collection addOptions = (Collection) m_addSelectDataSource.get(ps);
|
||||||
|
|
||||||
|
Iterator i;
|
||||||
|
Option option;
|
||||||
|
|
||||||
|
i = addOptions.iterator();
|
||||||
|
while ( i.hasNext() ) {
|
||||||
|
option = (Option) i.next();
|
||||||
|
optionsMap.put(option.getValue(), option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_removeSelectDataSource != null ) {
|
||||||
|
Collection removeOptions = (Collection) m_removeSelectDataSource.get(ps);
|
||||||
|
if ( removeOptions != null ) {
|
||||||
|
i = removeOptions.iterator();
|
||||||
|
while ( i.hasNext() ) {
|
||||||
|
option = (Option) i.next();
|
||||||
|
if (optionsMap.get(option.getValue()) == null) {
|
||||||
|
optionsMap.put(option.getValue(), option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateOptionValues(PageState ps, List addOptions,
|
||||||
|
List removeOptions,
|
||||||
|
HashMap m_optionsMap) {
|
||||||
|
Iterator iter;
|
||||||
|
|
||||||
|
iter = addOptions.iterator();
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
String s = (String) iter.next();
|
||||||
|
Option o = getOption(ps, m_optionsMap, s, LEFT);
|
||||||
|
// it is possible to be null if for some reason the key, s, is
|
||||||
|
// not found any of the maps
|
||||||
|
if (o != null) {
|
||||||
|
m_addSelect.addOption(o, ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = removeOptions.iterator();
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
String s = (String) iter.next();
|
||||||
|
Option o = getOption(ps, m_optionsMap, s, RIGHT);
|
||||||
|
// it is possible to be null if for some reason the key, s, is
|
||||||
|
// not found any of the maps
|
||||||
|
if (o != null) {
|
||||||
|
m_removeSelect.addOption(o, ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This looks at the request locals set in setRightMultipleSelectMap
|
||||||
|
* and setLeftMultipleSelectMap before falling back on the default
|
||||||
|
* mapping that was auto-generated. If the value is found
|
||||||
|
* in the passed in map then that value is used. Otherwise, the
|
||||||
|
* value is located in the default mapping
|
||||||
|
*/
|
||||||
|
private Option getOption(PageState state, Map optionMapping, String key,
|
||||||
|
int side) {
|
||||||
|
if (side == RIGHT) {
|
||||||
|
if (m_rightSelectMap != null) {
|
||||||
|
Map map = (Map)m_rightSelectMap.get(state);
|
||||||
|
if (map.get(key) != null) {
|
||||||
|
return (Option)map.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_leftSelectMap != null) {
|
||||||
|
Map map = (Map)m_leftSelectMap.get(state);
|
||||||
|
if (map.get(key) != null) {
|
||||||
|
return (Option)map.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (Option)optionMapping.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MultipleSelectPairFormProcessListener
|
||||||
|
implements FormProcessListener {
|
||||||
|
|
||||||
|
// This is to allow a call back to set an item as being
|
||||||
|
// initialized
|
||||||
|
private RequestLocal m_processed;
|
||||||
|
MultipleSelectPairFormProcessListener(RequestLocal processed) {
|
||||||
|
m_processed = processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(FormSectionEvent evt) {
|
||||||
|
PageState ps = evt.getPageState();
|
||||||
|
|
||||||
|
if (!m_addSubmit.isSelected(ps)
|
||||||
|
&& !m_removeSubmit.isSelected(ps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_processed.set(ps, Boolean.TRUE);
|
||||||
|
|
||||||
|
HashMap m_optionsMap;
|
||||||
|
List addOptions = new ArrayList();
|
||||||
|
List removeOptions = new ArrayList();
|
||||||
|
|
||||||
|
m_optionsMap = setupOptionMap(ps);
|
||||||
|
|
||||||
|
// We first update the array lists that contain the list
|
||||||
|
// of unselected options based on the contents of the
|
||||||
|
// hidden form variables.
|
||||||
|
updateUnselectedOptions(ps, addOptions, removeOptions);
|
||||||
|
|
||||||
|
// Then we update those array lists based on what the user
|
||||||
|
// moves.
|
||||||
|
if ( m_addSubmit.isSelected(ps) ) {
|
||||||
|
String[] selectedArray = (String[]) m_addSelect.getValue(ps);
|
||||||
|
if ( selectedArray != null ) {
|
||||||
|
List selectedAddOptions = Arrays.asList(selectedArray);
|
||||||
|
Iterator iter = selectedAddOptions.iterator();
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
String s = (String) iter.next();
|
||||||
|
// we only want to add the item if it has not
|
||||||
|
// already been added
|
||||||
|
if (!removeOptions.contains(s)) {
|
||||||
|
removeOptions.add(s);
|
||||||
|
}
|
||||||
|
if (leftSideChanges()) {
|
||||||
|
addOptions.remove(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_removeSubmit.isSelected(ps) ) {
|
||||||
|
String[] selectedArray = (String[]) m_removeSelect.getValue(ps);
|
||||||
|
if ( selectedArray != null ) {
|
||||||
|
List selectedRemoveOptions = Arrays.asList(selectedArray);
|
||||||
|
Iterator iter = selectedRemoveOptions.iterator();
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
String s = (String) iter.next();
|
||||||
|
removeOptions.remove(s);
|
||||||
|
// if the left side does not change then the
|
||||||
|
// item was never removed from the addOptions so
|
||||||
|
// it does not need to be added back.
|
||||||
|
if (leftSideChanges()) {
|
||||||
|
addOptions.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we put the full list of options back into the hidden.
|
||||||
|
// we have to convert this to a String[]...otherwise we
|
||||||
|
// can get a ClassCastException when used within a Wizard
|
||||||
|
String[] newValues = (String[]) addOptions.toArray(EMPTY_STRING_ARRAY);
|
||||||
|
m_addSelectOptions.setValue(ps, newValues);
|
||||||
|
|
||||||
|
// We do the same conversion for the new values
|
||||||
|
newValues = (String[]) removeOptions.toArray(EMPTY_STRING_ARRAY);
|
||||||
|
m_removeSelectOptions.setValue(ps, newValues);
|
||||||
|
|
||||||
|
// We finally generate the option values.
|
||||||
|
generateOptionValues(ps, addOptions, removeOptions, m_optionsMap);
|
||||||
|
m_addSelect.addOption(getEmptyOption(), ps);
|
||||||
|
m_removeSelect.addOption(getEmptyOption(), ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUnselectedOptions(PageState ps, List addOptions,
|
||||||
|
List removeOptions) {
|
||||||
|
// We add the unselected options back to the MultipleSelects.
|
||||||
|
String[] unselected = getUnselectedOptions(ps);
|
||||||
|
for (int i = 0; i < unselected.length; i++) {
|
||||||
|
String s = unselected[i];
|
||||||
|
addOptions.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] selected = getSelectedOptions(ps);
|
||||||
|
for (int i = 0; i < selected.length; i++) {
|
||||||
|
String s = selected[i];
|
||||||
|
removeOptions.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MultipleSelectPairFormInitListener implements FormInitListener {
|
||||||
|
private RequestLocal m_initialized;
|
||||||
|
MultipleSelectPairFormInitListener(RequestLocal initialized ) {
|
||||||
|
m_initialized = initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FormSectionEvent evt) {
|
||||||
|
PageState ps = evt.getPageState();
|
||||||
|
m_initialized.set(ps, Boolean.TRUE);
|
||||||
|
|
||||||
|
String[] addOptionsForHidden = EMPTY_STRING_ARRAY;
|
||||||
|
String[] removeOptionsForHidden = EMPTY_STRING_ARRAY;
|
||||||
|
|
||||||
|
Assert.exists(m_addSelectDataSource,
|
||||||
|
"You must provide some options for the " +
|
||||||
|
"user to choose!");
|
||||||
|
|
||||||
|
Collection addOptions = (Collection) m_addSelectDataSource.get(ps);
|
||||||
|
if (addOptions.size() > 0) {
|
||||||
|
Iterator iter = addOptions.iterator();
|
||||||
|
addOptionsForHidden = new String[addOptions.size()];
|
||||||
|
int idx = 0;
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
Option option = (Option) iter.next();
|
||||||
|
m_addSelect.addOption(option, ps);
|
||||||
|
addOptionsForHidden[idx++] = option.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_removeSelectDataSource != null ) {
|
||||||
|
Collection c = (Collection) m_removeSelectDataSource.get(ps);
|
||||||
|
if ( c != null && c.size() > 0 ) {
|
||||||
|
removeOptionsForHidden = new String[c.size()];
|
||||||
|
Iterator iter = c.iterator();
|
||||||
|
int idx = 0;
|
||||||
|
while ( iter.hasNext() ) {
|
||||||
|
Option option = (Option) iter.next();
|
||||||
|
m_removeSelect.addOption(option, ps);
|
||||||
|
removeOptionsForHidden[idx++] = option.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_addSelectOptions.setValue(ps, addOptionsForHidden);
|
||||||
|
m_removeSelectOptions.setValue(ps, removeOptionsForHidden);
|
||||||
|
m_addSelect.addOption(getEmptyOption(), ps);
|
||||||
|
m_removeSelect.addOption(getEmptyOption(), ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Option getEmptyOption() {
|
||||||
|
return new Option("",
|
||||||
|
new Label(" " +
|
||||||
|
" " +
|
||||||
|
" " +
|
||||||
|
" " +
|
||||||
|
" " +
|
||||||
|
" ",
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,317 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.DescriptiveComponent;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing an option of a widget.
|
||||||
|
*
|
||||||
|
* The Option consist of two parts:
|
||||||
|
* - a value, used by the background task to process the option
|
||||||
|
* - a display component, used to display the option to the user in the GUI,
|
||||||
|
* usually a Label (title), but may be e.g. an image as well.
|
||||||
|
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
public class Option extends DescriptiveComponent {
|
||||||
|
|
||||||
|
/** The value of the option, used by the background task to process the
|
||||||
|
* option.
|
||||||
|
* NOTE: The display component, the label, is provided by parent class! */
|
||||||
|
private String m_value;
|
||||||
|
/** The display component for the user in the GUI. It's usually a Label,
|
||||||
|
* but may be e.g. an image as well. */
|
||||||
|
private Component m_component;
|
||||||
|
private OptionGroup m_group;
|
||||||
|
private boolean m_isSelectOption;
|
||||||
|
|
||||||
|
// ///////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor Section
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (too) simple Constructor which uses a String as value as well as
|
||||||
|
* display component.
|
||||||
|
*
|
||||||
|
* @param value A String used as value as well as display component.
|
||||||
|
* @deprecated use Option(value,component) instead
|
||||||
|
*/
|
||||||
|
public Option(String value) {
|
||||||
|
this(value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor creates an Option whose label part consisting of a string.
|
||||||
|
* This results in a badly globalized label part. The localization depends
|
||||||
|
* on the language selected at the time the Option is created.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @param label
|
||||||
|
* @deprecated use Option(value,component) instead
|
||||||
|
*/
|
||||||
|
public Option(String value, String label) {
|
||||||
|
setValue(value);
|
||||||
|
setLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor creates an Option whose label part consisting of a Component,
|
||||||
|
* usually a Label(GlobalizedMessage).
|
||||||
|
* This constructor should be used to create a fully globalized and
|
||||||
|
* localized user interface.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @param label
|
||||||
|
*/
|
||||||
|
public Option(String value, Component label) {
|
||||||
|
setValue(value);
|
||||||
|
setComponent(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///////////////////////////////////////////////////////////////////////
|
||||||
|
// Getter/Setter Section
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value part of an option.
|
||||||
|
* @return the value part of this option.
|
||||||
|
*/
|
||||||
|
public final String getValue() {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets of modifies the value of on option.
|
||||||
|
* @param value new value part of the option
|
||||||
|
*/
|
||||||
|
public final void setValue(String value) {
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the display part of the option.
|
||||||
|
* @return the display component for this option
|
||||||
|
*/
|
||||||
|
public final Component getComponent() {
|
||||||
|
return m_component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets of modifies the display component of an option.
|
||||||
|
*
|
||||||
|
* @param component the display component for this option
|
||||||
|
*/
|
||||||
|
public final void setComponent(Component component) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_component = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets of modifies the display component of an option providing a Label.
|
||||||
|
* The label is internally stored as a component.
|
||||||
|
*
|
||||||
|
* @param label
|
||||||
|
*/
|
||||||
|
public final void setLabel(Label label) {
|
||||||
|
setComponent(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets the display component using a String. It results in a badly
|
||||||
|
* globalized UI
|
||||||
|
*
|
||||||
|
* @param label String to use as the display component
|
||||||
|
* @deprecated Use {@link #setComponent(Component component)} instead
|
||||||
|
*/
|
||||||
|
public final void setLabel(String label) {
|
||||||
|
setComponent(new Label(label));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param group
|
||||||
|
*/
|
||||||
|
public final void setGroup(OptionGroup group) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
Assert.exists(group);
|
||||||
|
m_group = group;
|
||||||
|
m_isSelectOption = BebopConstants.BEBOP_OPTION.equals(m_group.getOptionXMLElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final OptionGroup getGroup() {
|
||||||
|
return m_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the name (identifier) of the option group containing this
|
||||||
|
* option. Don't know the purpose of this.
|
||||||
|
*
|
||||||
|
* @return The name (identifier) of the option group this option belongs
|
||||||
|
* to
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return m_group.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONFOCUS</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnFocus(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_FOCUS,javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONBLUR</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnBlur(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_BLUR,javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONSELECT</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnSelect(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_SELECT,javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONCHANGE</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnChange(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_CHANGE,javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ON_KEY_UP</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
**/
|
||||||
|
|
||||||
|
public void setOnKeyUp(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_KEY_UP, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONCLICK</tt> attribute for the HTML tags that compose
|
||||||
|
* this element.
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnClick(String javascriptCode) {
|
||||||
|
setAttribute(Widget.ON_CLICK,javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParameterData getParameterData(PageState s) {
|
||||||
|
return m_group.getParameterData(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelected(ParameterData data) {
|
||||||
|
if (data == null || data.getValue() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Object value = data.getValue();
|
||||||
|
|
||||||
|
Object[] selectedValues;
|
||||||
|
if (value instanceof Object[]) {
|
||||||
|
selectedValues = (Object[])value;
|
||||||
|
} else {
|
||||||
|
selectedValues = new Object[] {value};
|
||||||
|
}
|
||||||
|
String optionValue = getValue();
|
||||||
|
|
||||||
|
if (optionValue == null || selectedValues == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Object selectedValue : selectedValues) {
|
||||||
|
if (selectedValue != null
|
||||||
|
&& optionValue.equalsIgnoreCase(selectedValue.toString())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate XML depending on what OptionGr.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState s, Element e) {
|
||||||
|
Element option = e.newChildElement(m_group.getOptionXMLElement(), BEBOP_XML_NS);
|
||||||
|
if ( ! m_isSelectOption ) {
|
||||||
|
option.addAttribute("name", getName());
|
||||||
|
}
|
||||||
|
option.addAttribute("value", getValue());
|
||||||
|
|
||||||
|
if (m_component != null) {
|
||||||
|
m_component.generateXML(s, option);
|
||||||
|
} else {
|
||||||
|
(new Label()).generateXML(s, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportAttributes(option);
|
||||||
|
if ( isSelected(getParameterData(s)) ) {
|
||||||
|
if ( m_isSelectOption ) {
|
||||||
|
option.addAttribute("selected", "selected");
|
||||||
|
} else {
|
||||||
|
option.addAttribute("checked", "checked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kludge to live with the fact that options don't do their own
|
||||||
|
* printing. Don't use this method, it will go away !
|
||||||
|
*
|
||||||
|
* @deprecated Will be removed without replacement once option handling
|
||||||
|
* has been refactored.
|
||||||
|
*/
|
||||||
|
final void generateAttributes(Element target) {
|
||||||
|
exportAttributes(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,574 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.RequestLocal;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModelWrapper;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.globalization.GlobalizationHelper;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import java.text.Collator;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing any widget that contains a list of options.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class OptionGroup extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(OptionGroup.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML element to be used by individual options belonging to this group.
|
||||||
|
* This variable has to be initialized by every subclass of OptionGroup.
|
||||||
|
* LEGACY: An abstract method would be the better design, but changing it
|
||||||
|
* would break the API.
|
||||||
|
*/
|
||||||
|
//protected String m_xmlElement;
|
||||||
|
|
||||||
|
// this only needs to be an ArrayList for multiple selection option groups
|
||||||
|
private List<String> m_selected;
|
||||||
|
private List<Option> m_options;
|
||||||
|
private Widget m_otherOption = null;
|
||||||
|
private Form m_form = null;
|
||||||
|
private boolean m_isDisabled = false;
|
||||||
|
private boolean m_isReadOnly = false;
|
||||||
|
/**
|
||||||
|
* Sort Mode for options
|
||||||
|
*/
|
||||||
|
private OptionGroup.SortMode sortMode;
|
||||||
|
/**
|
||||||
|
* Exclude first option from sorting?
|
||||||
|
*/
|
||||||
|
private boolean excludeFirst;
|
||||||
|
public static final String OTHER_OPTION = "__other__";
|
||||||
|
// this is only used for single selection option groups
|
||||||
|
|
||||||
|
private final static String TOO_MANY_OPTIONS_SELECTED
|
||||||
|
= "Only one option may be selected by default on this option group.";
|
||||||
|
|
||||||
|
|
||||||
|
/** request-local copy of selected elements, options */
|
||||||
|
private final RequestLocal m_requestOptions = new RequestLocal() {
|
||||||
|
@Override
|
||||||
|
public Object initialValue(final PageState state) {
|
||||||
|
return new ArrayList<Option>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final boolean isCompound() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ParameterModel for multiple OptionGroups is always an array parameter
|
||||||
|
* @param model
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
protected OptionGroup(final ParameterModel model) {
|
||||||
|
//super(model);
|
||||||
|
//m_options = new ArrayList<Option>();
|
||||||
|
//m_selected = new ArrayList<String>();
|
||||||
|
this(model, OptionGroup.SortMode.NO_SORT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OptionGroup(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode) {
|
||||||
|
this(model, sortMode, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OptionGroup(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode,
|
||||||
|
final boolean excludeFirst) {
|
||||||
|
super(model);
|
||||||
|
m_options = new ArrayList<Option>();
|
||||||
|
m_selected = new ArrayList<String>();
|
||||||
|
this.sortMode = sortMode;
|
||||||
|
this.excludeFirst = excludeFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator of all the default Options in this group.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Iterator<Option> getOptions() {
|
||||||
|
return m_options.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortMode {
|
||||||
|
|
||||||
|
NO_SORT,
|
||||||
|
ALPHABETICAL_ASCENDING,
|
||||||
|
ALPHABETICAL_DESENDING
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getOptionXMLElement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link Comparator} implementation is used to sort the list of options alphabetical. If
|
||||||
|
* the sorting is ascending or descending depends on the selected sort mode. The Comparator
|
||||||
|
* needs the {@link PageState} for retrieving the localised labels from the options.
|
||||||
|
*/
|
||||||
|
private class AlphabeticalSortComparator implements Comparator<Option> {
|
||||||
|
|
||||||
|
private final PageState state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor taking the current {@code PageState}.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
*/
|
||||||
|
public AlphabeticalSortComparator(final PageState state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(final Option option1, final Option option2) {
|
||||||
|
String label1;
|
||||||
|
String label2;
|
||||||
|
|
||||||
|
//Check if the first option to compare has a inner label component. If it has
|
||||||
|
//store the localised text. Otherwise use the name of the option.
|
||||||
|
if (option1.getComponent() instanceof Label) {
|
||||||
|
final Label label = (Label) option1.getComponent();
|
||||||
|
label1 = label.getLabel(state);
|
||||||
|
} else {
|
||||||
|
label1 = option1.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same for the second option
|
||||||
|
if (option2.getComponent() instanceof Label) {
|
||||||
|
final Label label = (Label) option2.getComponent();
|
||||||
|
label2 = label.getLabel(state);
|
||||||
|
} else {
|
||||||
|
label2 = option2.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
//We are using a Collator instance here instead of String#compare(String) because
|
||||||
|
//String#compare(String) is not local sensitive. For example in german a word starting
|
||||||
|
//with the letter 'Ö' should be handled like a word starting with the letter 'O'.
|
||||||
|
//Using String#compare(String) would put them at the end of the list.
|
||||||
|
//Depending on the sort mode we compare label1 with label2 (ascending) or label2 with
|
||||||
|
//label1 (descending).
|
||||||
|
final Collator collator = Collator
|
||||||
|
.getInstance(GlobalizationHelper.getNegotiatedLocale());
|
||||||
|
if (sortMode == SortMode.ALPHABETICAL_ASCENDING) {
|
||||||
|
return collator.compare(label1, label2);
|
||||||
|
} else if (sortMode == SortMode.ALPHABETICAL_DESENDING) {
|
||||||
|
return collator.compare(label2, label1);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator of all the default Options in this group, plus any request-specific
|
||||||
|
* options.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Iterator<Option> getOptions(final PageState state) {
|
||||||
|
List<Option> allOptions = new ArrayList<Option>();
|
||||||
|
allOptions.addAll(m_options);
|
||||||
|
List<Option> requestOptions = (List<Option>) m_requestOptions.get(state);
|
||||||
|
for (Iterator<Option> iterator = requestOptions.iterator(); iterator.hasNext();) {
|
||||||
|
final Option option = iterator.next();
|
||||||
|
if (!allOptions.contains(option)) {
|
||||||
|
allOptions.add(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allOptions.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOptions() {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_options = new ArrayList<Option>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new option.
|
||||||
|
*
|
||||||
|
* @param option The {@link Option} to be added. Note: the argument is modified and associated
|
||||||
|
* with this OptionGroup, regardless of what its group was.
|
||||||
|
*/
|
||||||
|
public void addOption(final Option option) {
|
||||||
|
addOption(option, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new option.
|
||||||
|
*
|
||||||
|
* @param opt
|
||||||
|
* @param ps
|
||||||
|
*/
|
||||||
|
public void addOption(final Option option, final PageState state) {
|
||||||
|
addOption(option, state, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new option at the beginning of the list.
|
||||||
|
*
|
||||||
|
* @param option The {@link Option} to be added. Note: the argument is modified and associated
|
||||||
|
* with this OptionGroup, regardless of what its group was.
|
||||||
|
*/
|
||||||
|
public void prependOption(final Option option) {
|
||||||
|
addOption(option, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prependOption(final Option option, final PageState state) {
|
||||||
|
addOption(option, state, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOption(final Option option) {
|
||||||
|
removeOption(option, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new option for the scope of the current request, or to the page
|
||||||
|
* as a whole if there is no current request.
|
||||||
|
*
|
||||||
|
* @param option The {@link Option} to be added. Note: the argument is modified and associated
|
||||||
|
* with this OptionGroup, regardless of what its group was.
|
||||||
|
* @param state the current page state. if ps is null, adds option to the default option list.
|
||||||
|
* @param prepend If true, prepend option to the list instead of appending it
|
||||||
|
*/
|
||||||
|
public void addOption(final Option option, final PageState state, final boolean prepend) {
|
||||||
|
List<Option> list = m_options;
|
||||||
|
if (state == null) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
} else {
|
||||||
|
list = (List<Option>) m_requestOptions.get(state);
|
||||||
|
}
|
||||||
|
option.setGroup(this);
|
||||||
|
|
||||||
|
if (prepend == true) {
|
||||||
|
list.add(0, option);
|
||||||
|
} else {
|
||||||
|
list.add(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOption(final Option option, final PageState state) {
|
||||||
|
List<Option> list = m_options;
|
||||||
|
if (state == null) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
} else {
|
||||||
|
list = (List<Option>) m_requestOptions.get(state);
|
||||||
|
}
|
||||||
|
list.remove(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOption(String key) {
|
||||||
|
removeOption(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the first option whose key is isEqual to the key that is
|
||||||
|
* passed in.
|
||||||
|
* @param key
|
||||||
|
* @param state the current page state. if ps is null, adds option to
|
||||||
|
* the default option list.
|
||||||
|
*/
|
||||||
|
public void removeOption(final String key, final PageState state) {
|
||||||
|
// This is not an entirely efficient technique. A more
|
||||||
|
// efficient solution is to switch to using a HashMap.
|
||||||
|
List<Option> list = m_options;
|
||||||
|
if (state == null) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
} else {
|
||||||
|
list = (List<Option>) m_requestOptions.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<Option> iterator = list.iterator();
|
||||||
|
Option option;
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
option = iterator.next();
|
||||||
|
if (option.getValue().equals(key)) {
|
||||||
|
list.remove(option);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an "Other (please specify)" type option to the widget
|
||||||
|
*
|
||||||
|
* @param label
|
||||||
|
* @param width The width, in characters, of the "Other" entry area
|
||||||
|
* @param height The height, in characters, of the "Other" entry area. If this is 1 then a
|
||||||
|
* TextField is used. Otherwise a TextArea is used.
|
||||||
|
*/
|
||||||
|
public void addOtherOption(final String label, final int width, final int height) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
|
||||||
|
final Option otherOption = new Option(OTHER_OPTION, label);
|
||||||
|
addOption(otherOption);
|
||||||
|
|
||||||
|
final ParameterModel model = getParameterModel();
|
||||||
|
|
||||||
|
if (1 == height) {
|
||||||
|
TextField field = new TextField(model.getName() + ".other");
|
||||||
|
field.setSize(width);
|
||||||
|
|
||||||
|
m_otherOption = field;
|
||||||
|
} else {
|
||||||
|
TextArea area = new TextArea(model.getName() + ".other");
|
||||||
|
area.setCols(width);
|
||||||
|
area.setRows(height);
|
||||||
|
|
||||||
|
m_otherOption = area;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != m_form) {
|
||||||
|
m_otherOption.setForm(m_form);
|
||||||
|
|
||||||
|
if (m_isDisabled) {
|
||||||
|
m_otherOption.setDisabled();
|
||||||
|
}
|
||||||
|
if (m_isReadOnly) {
|
||||||
|
m_otherOption.setReadOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setParameterModel(new ParameterModelWrapper(model) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParameterData createParameterData(final HttpServletRequest request,
|
||||||
|
Object defaultValue,
|
||||||
|
boolean isSubmission) {
|
||||||
|
|
||||||
|
final String[] values = request.getParameterValues(getName());
|
||||||
|
String[] otherValues = request.getParameterValues(getName() + ".other");
|
||||||
|
|
||||||
|
String other = (null == otherValues) ? null : otherValues[0];
|
||||||
|
|
||||||
|
if (null != values) {
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
if (OTHER_OPTION.equals(values[i])) {
|
||||||
|
values[i] = other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.debug("createParameterData in OptionGroup");
|
||||||
|
|
||||||
|
return super.createParameterData(new HttpServletRequestWrapper(request) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getParameterValues(String key) {
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Getting values for " + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.getName().equals(key)) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return super.getParameterValues(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, defaultValue, isSubmission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceOther(String[] values, String other) {
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an option selected by default. Updates the parameter model for
|
||||||
|
* the option group accordingly.
|
||||||
|
*
|
||||||
|
* @param value the value of the option to be added to the
|
||||||
|
* by-default-selected set.
|
||||||
|
*/
|
||||||
|
public void setOptionSelected(final String value) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if (!isMultiple()) {
|
||||||
|
// only one option may be selected
|
||||||
|
// to this selected list better be empty
|
||||||
|
Assert.isTrue(m_selected.isEmpty(), TOO_MANY_OPTIONS_SELECTED);
|
||||||
|
m_selected.add(value);
|
||||||
|
getParameterModel().setDefaultValue(value);
|
||||||
|
} else {
|
||||||
|
m_selected.add(value);
|
||||||
|
getParameterModel().setDefaultValue(m_selected.toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make an option selected by default
|
||||||
|
*
|
||||||
|
* @param option the option to be added to the by-default-selected set.
|
||||||
|
*/
|
||||||
|
public void setOptionSelected(Option option) {
|
||||||
|
setOptionSelected(option.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
final OptionGroup cloned = (OptionGroup) super.clone();
|
||||||
|
//cloned.m_options = m_options.clone();
|
||||||
|
//cloned.m_selected = m_selected.clone();
|
||||||
|
cloned.m_options.addAll(m_options);
|
||||||
|
cloned.m_selected.addAll(m_selected);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this a multiple (and not single) selection option group.
|
||||||
|
* Note that this should really be declared abstract, but we can't because
|
||||||
|
* it used to be in the direct subclass Select and making it abstract could
|
||||||
|
* break other subclasses that don't declare isMultiple. So we have a
|
||||||
|
* trivial implementation instead.
|
||||||
|
*
|
||||||
|
* @return true if this OptionGroup can have more than one selected option;
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isMultiple() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisabled() {
|
||||||
|
m_isDisabled = true;
|
||||||
|
|
||||||
|
if (null != m_otherOption) {
|
||||||
|
m_otherOption.setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly() {
|
||||||
|
m_isReadOnly = true;
|
||||||
|
|
||||||
|
if (null != m_otherOption) {
|
||||||
|
m_otherOption.setReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setForm(final Form form) {
|
||||||
|
m_form = form;
|
||||||
|
if (null != m_otherOption) {
|
||||||
|
m_otherOption.setForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the DOM for the select widget
|
||||||
|
* <p>
|
||||||
|
* Generates DOM fragment:
|
||||||
|
* <p>
|
||||||
|
* <
|
||||||
|
* pre><code><bebop:* name=... [onXXX=...]>
|
||||||
|
* <bebop:option name=... [selected]> option value </bebop:option%gt;
|
||||||
|
* ...
|
||||||
|
* </bebop:*select></code></pre>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateWidget(final PageState state, final Element parent) {
|
||||||
|
final Element optionGroup = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
optionGroup.addAttribute("name", getName());
|
||||||
|
optionGroup.addAttribute("class", getName().replace(".", " "));
|
||||||
|
// Localized title for this option group
|
||||||
|
if (getLabel() != null) {
|
||||||
|
optionGroup.addAttribute("label", (String)getLabel()
|
||||||
|
.localize(state.getRequest()));
|
||||||
|
}
|
||||||
|
if (isMultiple()) {
|
||||||
|
optionGroup.addAttribute("multiple", "multiple");
|
||||||
|
}
|
||||||
|
exportAttributes(optionGroup);
|
||||||
|
|
||||||
|
//Build a list of all options we can operator on.
|
||||||
|
final List<Option> options = new ArrayList<Option>();
|
||||||
|
for (Iterator<Option> iterator = getOptions(state); iterator.hasNext();) {
|
||||||
|
options.add(iterator.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the sort mode is not {@code NO_SORT}, sort the the list.
|
||||||
|
if (sortMode != SortMode.NO_SORT) {
|
||||||
|
|
||||||
|
//If exclude first is sest to true the first option should stay on the top.
|
||||||
|
//We simply remove the first option from our list and generate the XML for it here.
|
||||||
|
if (excludeFirst && !options.isEmpty()) {
|
||||||
|
final Option first = options.remove(0);
|
||||||
|
first.generateXML(state, optionGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort the list using our {@link AlphabeticalSortComparator}.
|
||||||
|
Collections.sort(options, new AlphabeticalSortComparator(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate the XML for the options.
|
||||||
|
for (Option option : options) {
|
||||||
|
option.generateXML(state, optionGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for (Iterator<Option> iterator = getOptions(state); iterator.hasNext();) {
|
||||||
|
// Option option = iterator.next();
|
||||||
|
// option.generateXML(state, optionGroup);
|
||||||
|
// }
|
||||||
|
if (null != m_otherOption) {
|
||||||
|
m_otherOption.generateXML(state, optionGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a password entry field in an HTML form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Password extends Widget {
|
||||||
|
|
||||||
|
public Password(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Password(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>MAXLENGTH</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setMaxLength(int length) {
|
||||||
|
setAttribute("maxlength",String.valueOf(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>SIZE</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setSize(int size) {
|
||||||
|
setAttribute("size",String.valueOf(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method for rendering this Password widget in a visitor.
|
||||||
|
|
||||||
|
public void accept(FormVisitor visitor) throws IOException {
|
||||||
|
visitor.visitPassword(this);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class
|
||||||
|
* representing a <em>group</em> of associated radio buttons.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$ */
|
||||||
|
public class RadioGroup extends OptionGroup implements BebopConstants {
|
||||||
|
|
||||||
|
// xml attribute for layout
|
||||||
|
private final static String AXIS = "axis";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that options should be laid out left to right.
|
||||||
|
*/
|
||||||
|
// this is default
|
||||||
|
public final static int HORIZONTAL = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that options should be laid out top to bottom.
|
||||||
|
*/
|
||||||
|
public final static int VERTICAL = 2;
|
||||||
|
|
||||||
|
public RadioGroup(String name) {
|
||||||
|
this(new StringParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RadioGroup(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
//m_xmlElement = BEBOP_RADIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "radio";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The XML tag.
|
||||||
|
* @return The tag to be used for the top level DOM element
|
||||||
|
* generated for this type of Widget. */
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_RADIOGROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOptionXMLElement() {
|
||||||
|
return BEBOP_RADIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a multiple (and not single) selection option group?
|
||||||
|
*
|
||||||
|
* @return true if this OptionGroup can have more than one
|
||||||
|
* selected option; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isMultiple() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the layout for the options in this radio group.
|
||||||
|
*
|
||||||
|
* @param layout one of RadioGroup.VERTICAL or RadioGroup.HORIZONTAL
|
||||||
|
**/
|
||||||
|
public void setLayout(int layout) {
|
||||||
|
setAttribute(AXIS, String.valueOf(layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layout for the options in this radio group.
|
||||||
|
*
|
||||||
|
* @return one of RadioGroup.VERTICAL or RadioGroup.HORIZONTAL
|
||||||
|
**/
|
||||||
|
public int getLayout() {
|
||||||
|
String value = getAttribute(AXIS);
|
||||||
|
if (value == null) {
|
||||||
|
return HORIZONTAL;
|
||||||
|
} else if (value.equals(String.valueOf(HORIZONTAL))) {
|
||||||
|
return HORIZONTAL;
|
||||||
|
} else if (value.equals(String.valueOf(VERTICAL))) {
|
||||||
|
return VERTICAL;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"invalid value for axis attribute: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a reset button in an HTML form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Reset extends Widget {
|
||||||
|
|
||||||
|
public Reset(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reset (ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
protected String getType() {
|
||||||
|
return "reset";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,484 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.event.PageEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
|
import com.arsdigita.bebop.event.SearchAndSelectListener;
|
||||||
|
import com.arsdigita.bebop.event.SearchAndSelectModel;
|
||||||
|
import com.arsdigita.bebop.event.FormValidationListener;
|
||||||
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
|
import com.arsdigita.bebop.FormSection;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.ArrayParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import java.util.TooManyListenersException;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search and select Bebop widget. This widget is used to allow a user to search
|
||||||
|
* for a particular item over a potentially very large set. Depending on the
|
||||||
|
* size of the dataset, the user will either see a search box or a selection box
|
||||||
|
* (with all valid items). The search box will then change to a selection box
|
||||||
|
* once the user submits the form, allowing them then to choose the items they
|
||||||
|
* desire.
|
||||||
|
* <p>
|
||||||
|
* The datasource for SearchAndSelect is provided by an implentation of the
|
||||||
|
* SearchAndSelectModel interface. SAMPLE IMPLEMENTATION GOES HERE
|
||||||
|
*
|
||||||
|
* @author Patrick McNeill
|
||||||
|
* @version $Id$
|
||||||
|
* @since 4.5
|
||||||
|
*/
|
||||||
|
public class SearchAndSelect extends FormSection
|
||||||
|
implements BebopConstants, PrintListener {
|
||||||
|
|
||||||
|
private static final Logger s_cat
|
||||||
|
= Logger.getLogger(SearchAndSelect.class);
|
||||||
|
|
||||||
|
protected String m_name;
|
||||||
|
// name of this super-widget
|
||||||
|
|
||||||
|
protected String m_value = "";
|
||||||
|
// current value of the widget
|
||||||
|
|
||||||
|
protected String m_query = "";
|
||||||
|
// the query to search for this go round. set by the form validation
|
||||||
|
// listener.
|
||||||
|
|
||||||
|
protected int m_maxViewableResults = 10;
|
||||||
|
// number of hits before the search widget pops up, eventually should be
|
||||||
|
// a system parameter
|
||||||
|
|
||||||
|
protected SearchAndSelectModel m_results = null;
|
||||||
|
// interface to the dataset
|
||||||
|
|
||||||
|
protected SearchAndSelectListener m_listener = null;
|
||||||
|
|
||||||
|
protected boolean m_isMultiple = false;
|
||||||
|
// multiselect?
|
||||||
|
|
||||||
|
protected boolean m_useCheckboxes = false;
|
||||||
|
// use checkboxes or multiple-select for the multiple case
|
||||||
|
|
||||||
|
protected boolean m_isOptional = true;
|
||||||
|
// optional?
|
||||||
|
|
||||||
|
protected Object m_this = this;
|
||||||
|
// so "this" will work in my anonymous inner classes
|
||||||
|
|
||||||
|
protected TextField m_outputTextWidget;
|
||||||
|
protected Widget m_outputSelectWidget;
|
||||||
|
// internal widgets used render either a text box, a checkbox, or a select
|
||||||
|
|
||||||
|
protected Hidden m_oldValueWidget;
|
||||||
|
// internal Hidden widget used to save the previous search
|
||||||
|
|
||||||
|
protected String m_oldValue = "";
|
||||||
|
// the contents of the oldValueWidget, set by the validation listener
|
||||||
|
|
||||||
|
protected boolean m_isSearchLocked = false;
|
||||||
|
// true if the user has already seen a checkbox group or select box,
|
||||||
|
// false otherwise. determines if the user is still refining his/her search
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates the output widgets and adds them all to the form
|
||||||
|
*/
|
||||||
|
private void initializeOutputWidget() {
|
||||||
|
m_oldValueWidget = new Hidden(getName() + ".oldvalue");
|
||||||
|
add(m_oldValueWidget);
|
||||||
|
|
||||||
|
m_outputTextWidget = new TextField(getName() + ".text");
|
||||||
|
add(m_outputTextWidget);
|
||||||
|
|
||||||
|
if (m_isMultiple) {
|
||||||
|
if (m_useCheckboxes) {
|
||||||
|
m_outputSelectWidget = new CheckboxGroup(getName() + ".select");
|
||||||
|
} else {
|
||||||
|
m_outputSelectWidget
|
||||||
|
= new MultipleSelect(getName() + ".select");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_outputSelectWidget = new SingleSelect(getName() + ".select");
|
||||||
|
}
|
||||||
|
add(m_outputSelectWidget);
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_outputSelectWidget.addPrintListener(this);
|
||||||
|
} catch (TooManyListenersException e) {
|
||||||
|
s_cat.error("Could not add print listener", e);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
s_cat.error("Could not add print listener", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare(PrintEvent e) {
|
||||||
|
if (m_results == null) {
|
||||||
|
m_results = m_listener.getModel(new PageEvent(this, e.getPageState()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_results == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_results.setQuery(m_query);
|
||||||
|
|
||||||
|
if (m_isSearchLocked
|
||||||
|
|| (((!m_oldValue.equals("")
|
||||||
|
&& m_oldValue.equals(m_value))
|
||||||
|
|| (m_maxViewableResults >= m_results.resultsCount()))
|
||||||
|
&& (m_results.resultsCount() > 0))) {
|
||||||
|
|
||||||
|
OptionGroup outputWidget = (OptionGroup) e.getTarget();
|
||||||
|
outputWidget.clearOptions();
|
||||||
|
|
||||||
|
if (m_isOptional && !m_isMultiple) {
|
||||||
|
outputWidget.addOption(new Option("", "None"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_results.resultsCount(); i++) {
|
||||||
|
outputWidget.addOption(
|
||||||
|
new Option(m_results.getID(i), m_results.getLabel(i)));
|
||||||
|
|
||||||
|
s_cat.debug(" " + m_results.getID(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SearchAndSelect widget to select a single value.
|
||||||
|
*
|
||||||
|
* @param name the name of the widget
|
||||||
|
*/
|
||||||
|
public SearchAndSelect(String name) {
|
||||||
|
this(name, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SearchAndSelect widget with the specified name and
|
||||||
|
* SearchAndSelectModel.
|
||||||
|
*
|
||||||
|
* @param name the name of the widget
|
||||||
|
* @param isMultiple whether or not the widget accepts multiple values
|
||||||
|
* @param useCheckboxes use checkboxes or a multiselect
|
||||||
|
*/
|
||||||
|
public SearchAndSelect(String name,
|
||||||
|
boolean isMultiple) {
|
||||||
|
this(name, isMultiple, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SearchAndSelect widget with the specified name and
|
||||||
|
* SearchAndSelectModel.
|
||||||
|
*
|
||||||
|
* @param name the name of the widget
|
||||||
|
* @param isMultiple whether or not the widget accepts multiple values
|
||||||
|
* @param useCheckboxes use checkboxes or a multiselect
|
||||||
|
*/
|
||||||
|
public SearchAndSelect(String name,
|
||||||
|
boolean isMultiple,
|
||||||
|
boolean useCheckboxes) {
|
||||||
|
|
||||||
|
super(new SimpleContainer());
|
||||||
|
|
||||||
|
m_isMultiple = isMultiple;
|
||||||
|
m_useCheckboxes = useCheckboxes;
|
||||||
|
m_name = name;
|
||||||
|
|
||||||
|
initializeOutputWidget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a form validation listener so that we can determine whether
|
||||||
|
* or not the form is valid. This is required because this widget
|
||||||
|
* needs an arbitrary number of page loads to succeed. Also note
|
||||||
|
* that the error messages generated here are not displayed to the
|
||||||
|
* user as the error field is also used as a help field.
|
||||||
|
*/
|
||||||
|
super.addValidationListener(new FormValidationListener() {
|
||||||
|
@Override
|
||||||
|
public void validate(FormSectionEvent e) {
|
||||||
|
FormData data = e.getFormData();
|
||||||
|
|
||||||
|
m_results = m_listener.getModel(
|
||||||
|
new PageEvent(m_this, e.getPageState()));
|
||||||
|
|
||||||
|
if (m_results == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_oldValue = data.getString(getName() + ".oldvalue");
|
||||||
|
|
||||||
|
m_value = data.getString(getName() + ".text");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine what stage in the process we're at. If .text
|
||||||
|
* is null, then check what the select/checkbox shows.
|
||||||
|
*/
|
||||||
|
if (m_value == null) {
|
||||||
|
m_isSearchLocked = true;
|
||||||
|
m_query = m_oldValue;
|
||||||
|
|
||||||
|
if (m_isMultiple) {
|
||||||
|
String[] tmpArray = (String[]) data
|
||||||
|
.get(getName() + ".select");
|
||||||
|
if (tmpArray == null) {
|
||||||
|
m_value = "";
|
||||||
|
} else {
|
||||||
|
m_value = tmpArray[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_value = data.getString(getName() + ".select");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_query = m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If optional and nothing selected, we're done
|
||||||
|
*/
|
||||||
|
if (m_value.equals("") && m_isOptional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String oldQuery = m_results.getQuery();
|
||||||
|
|
||||||
|
m_results.setQuery(m_query);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If search returns only one hit and is a non-optional single
|
||||||
|
* select, it's done.
|
||||||
|
*/
|
||||||
|
if (!m_isOptional
|
||||||
|
&& !m_isMultiple
|
||||||
|
&& (m_results.resultsCount() == 1)) {
|
||||||
|
m_isSearchLocked = true;
|
||||||
|
m_value = m_results.getID(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're in the results phase, determine what the user
|
||||||
|
* chose
|
||||||
|
*/
|
||||||
|
if (m_isSearchLocked) {
|
||||||
|
if (!m_isMultiple) {
|
||||||
|
StringParameter param
|
||||||
|
= new StringParameter(getName());
|
||||||
|
|
||||||
|
data.setParameter(getName(),
|
||||||
|
new ParameterData(param, m_value));
|
||||||
|
} else {
|
||||||
|
ArrayParameter param
|
||||||
|
= new ArrayParameter(getName());
|
||||||
|
String[] tmpArray = (String[]) data
|
||||||
|
.get(getName() + ".select");
|
||||||
|
|
||||||
|
if (tmpArray == null) {
|
||||||
|
tmpArray = new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
data.setParameter(getName(),
|
||||||
|
new ParameterData(param, tmpArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
data.addError("Search not complete yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_results.setQuery(oldQuery);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setSearchAndSelectListener(SearchAndSelectListener listener) {
|
||||||
|
m_listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the widget.
|
||||||
|
*
|
||||||
|
* @return the name of the widget
|
||||||
|
*/
|
||||||
|
public final String getName() {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal function to retrieve a single text value for the widget.
|
||||||
|
*/
|
||||||
|
private String getTextValue(PageState state) {
|
||||||
|
if (m_value != null) {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isSearchLocked) {
|
||||||
|
if (m_isMultiple) {
|
||||||
|
return ((String[]) m_outputSelectWidget.getValue(state))[0];
|
||||||
|
} else {
|
||||||
|
return (String) m_outputSelectWidget.getValue(state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (String) m_outputTextWidget.getValue(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the type of HTML form element to create. This will not
|
||||||
|
* necessarily be accurate until generateWidget is called as the query will
|
||||||
|
* be unavailable until that point.
|
||||||
|
*
|
||||||
|
* @return "text" or "select" depending on the result size
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
if (m_isSearchLocked) {
|
||||||
|
return m_outputSelectWidget.getType();
|
||||||
|
} else {
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is a multiple select widget, or single select.
|
||||||
|
*
|
||||||
|
* @return boolean -- true for multiple, false for single
|
||||||
|
*/
|
||||||
|
public final boolean isMultiple() {
|
||||||
|
return m_isMultiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this is an optional widget
|
||||||
|
*
|
||||||
|
* @return true for optional, false otherwise
|
||||||
|
*/
|
||||||
|
public final boolean isOptional() {
|
||||||
|
return m_isOptional;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify whether or not the widget is optional.
|
||||||
|
*
|
||||||
|
* @param isOptional true for optional, false for required
|
||||||
|
*/
|
||||||
|
public SearchAndSelect setOptional(boolean isOptional) {
|
||||||
|
m_isOptional = isOptional;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the widget is composed of multiple HTML elements. Always
|
||||||
|
* returns true, as the widget makes use of a hidden element and another
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
public boolean isCompound() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML datastructure for this widget. Adds a hidden, a
|
||||||
|
* textbox, checkbox group, or select, and possibly some number of
|
||||||
|
* formErrors.
|
||||||
|
*
|
||||||
|
* @param state the state of the page
|
||||||
|
* @param parent the parent widget
|
||||||
|
*/
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
if (m_results == null) {
|
||||||
|
m_results = m_listener.getModel(new PageEvent(this, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_results == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_isSearchLocked
|
||||||
|
|| (((!m_oldValue.equals("")
|
||||||
|
&& m_oldValue.equals(m_value))
|
||||||
|
|| (m_maxViewableResults >= m_results.resultsCount()))
|
||||||
|
&& (m_results.resultsCount() > 0))) {
|
||||||
|
m_outputSelectWidget.generateXML(state, parent);
|
||||||
|
} else {
|
||||||
|
m_outputTextWidget.generateXML(state, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_oldValueWidget.setValue(state, m_query);
|
||||||
|
|
||||||
|
m_oldValueWidget.generateXML(state, parent);
|
||||||
|
|
||||||
|
generateErrors(state, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the error messages for this widget. This widget has some
|
||||||
|
* specialized error messages, so it is necessary to override the default
|
||||||
|
* error generator. Basically, the m_results field won't be available
|
||||||
|
* outside this class, so this needs to be internal.
|
||||||
|
*
|
||||||
|
* @param state the state of the page
|
||||||
|
* @param parent the parent widget
|
||||||
|
*/
|
||||||
|
protected void generateErrors(PageState state, Element parent) {
|
||||||
|
String curValue = getTextValue(state);
|
||||||
|
|
||||||
|
if (m_results == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_results.resultsCount() > m_maxViewableResults) {
|
||||||
|
|
||||||
|
Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS);
|
||||||
|
|
||||||
|
if ((curValue == null) || (curValue.equals(""))) {
|
||||||
|
error.addAttribute("message",
|
||||||
|
"Please enter a comma-delimited search");
|
||||||
|
} else if ((!m_oldValue.equals(curValue))
|
||||||
|
&& !m_isSearchLocked) {
|
||||||
|
error.addAttribute("message",
|
||||||
|
"Your search returned "
|
||||||
|
+ m_results.resultsCount() + " matches. "
|
||||||
|
+ "Please refine your search or leave the "
|
||||||
|
+ "search as it is to see all results.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_results.resultsCount() == 0) {
|
||||||
|
if (!curValue.equals("")) {
|
||||||
|
Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS);
|
||||||
|
error.addAttribute("message",
|
||||||
|
"Your search returned no matches. Please "
|
||||||
|
+ "try again");
|
||||||
|
} else {
|
||||||
|
Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS);
|
||||||
|
error.addAttribute("message", "WARNING -- NO DATA FOUND");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class
|
||||||
|
* representing an HTML <code>SELECT</code> element.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$ */
|
||||||
|
public abstract class Select extends OptionGroup implements BebopConstants {
|
||||||
|
|
||||||
|
public Select(final ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
//m_xmlElement = BEBOP_OPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Select(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode) {
|
||||||
|
super(model, sortMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Select(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode,
|
||||||
|
final boolean excludeFirst) {
|
||||||
|
super(model, sortMode, excludeFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "select";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTML size attribute of this widget.
|
||||||
|
*
|
||||||
|
* @param n The number of visible rows in the widget
|
||||||
|
*/
|
||||||
|
public void setSize(int n) {
|
||||||
|
setAttribute("size", Integer.toString(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method for rendering this Select widget in a visitor.
|
||||||
|
*/
|
||||||
|
/* public void accept(FormVisitor visitor) throws IOException {
|
||||||
|
visitor.visitSelect(this);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/** The XML tag.
|
||||||
|
* @return The tag to be used for the top level DOM element
|
||||||
|
* generated for this type of Widget. */
|
||||||
|
protected abstract String getElementTag();
|
||||||
|
|
||||||
|
public String getOptionXMLElement() {
|
||||||
|
return BEBOP_OPTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing an HTML <code>SELECT</code> element with a single selection.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @author Christian Brechbühler (christian@arsdigita.com)
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class SingleSelect extends Select {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML tag.
|
||||||
|
*
|
||||||
|
* @return The tag to be used for the top level DOM element generated for this type of Widget.
|
||||||
|
*/
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_SELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SingleSelect widget, using a StringParameter model with the given parameter
|
||||||
|
* name. Since you can only have one item selected from a SingleSelect, the string parameter
|
||||||
|
* returns the value of the selected option.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to <code>SingleSelect(new StringParameter(name))</code>
|
||||||
|
*
|
||||||
|
* @param name the name of the string parameter
|
||||||
|
*/
|
||||||
|
public SingleSelect(final String name) {
|
||||||
|
super(new StringParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SingleSelect widget, using the given parameter model.
|
||||||
|
*
|
||||||
|
* @param model the parameter model
|
||||||
|
*/
|
||||||
|
public SingleSelect(final ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SingleSelect(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode) {
|
||||||
|
super(model, sortMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SingleSelect(final ParameterModel model,
|
||||||
|
final OptionGroup.SortMode sortMode,
|
||||||
|
final boolean excludeFirst) {
|
||||||
|
super(model, sortMode, excludeFirst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State that this is a single select
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isMultiple() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.FormModel;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit buttons on HTML forms. The button will only do anything
|
||||||
|
* useful if it is contained directly or indirectky in a {@link
|
||||||
|
* com.arsdigita.bebop.Form}.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Submit extends Widget {
|
||||||
|
|
||||||
|
private GlobalizedMessage m_buttonLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new submit button. The button will use <code>name</code>
|
||||||
|
* for both its name attribute and as the label displayed to the
|
||||||
|
* user.
|
||||||
|
*
|
||||||
|
* @param name the button's name and label
|
||||||
|
* @pre name != null
|
||||||
|
* @deprecated use Submit(GlobalizedMessage) or even better
|
||||||
|
* Submit(name, GlobalizedMessage) instead
|
||||||
|
*/
|
||||||
|
public Submit(String name) {
|
||||||
|
// To pacify the com.arsdigita.web.ParameterMap#validateName(String)
|
||||||
|
// method, get rid of spaces.
|
||||||
|
this(name.replace(' ', '_'), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new submit button.
|
||||||
|
*
|
||||||
|
* @param name the button's name
|
||||||
|
* @param label the label displayed on the button
|
||||||
|
* @deprecated use Submit(name, GlobalizedMessage instead
|
||||||
|
*/
|
||||||
|
public Submit(String name, String label) {
|
||||||
|
super(name);
|
||||||
|
setButtonLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Create a new submit button.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param label the label displayed on the button
|
||||||
|
*/
|
||||||
|
public Submit(GlobalizedMessage label) {
|
||||||
|
super(label.getKey());
|
||||||
|
setButtonLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Create a new submit button.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param name the button's name
|
||||||
|
* @param label the label displayed on the button
|
||||||
|
*/
|
||||||
|
public Submit(String name, GlobalizedMessage label) {
|
||||||
|
super(name);
|
||||||
|
setButtonLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new submit button.
|
||||||
|
*
|
||||||
|
* @param model a <code>ParameterModel</code> value
|
||||||
|
*/
|
||||||
|
public Submit(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
setButtonLabel(model.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "submit";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether or not javascript should be included in the
|
||||||
|
* onClick field to try to prevent doubleclicks.
|
||||||
|
*
|
||||||
|
* @param avoid true to avoid doubleclicks, false otherwise.
|
||||||
|
* @deprecated use configuration parameter waf.bebop.dcp_message
|
||||||
|
* to enable/disable double-click protection globally.
|
||||||
|
* In case you want to disable DCP on per-widget basis,
|
||||||
|
* use {@link #setOnClick(String)}.
|
||||||
|
* @see #setOnClick(String)
|
||||||
|
*/
|
||||||
|
public void avoidDoubleClick ( boolean avoid ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets the text that will be displayed on the actual button.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param buttonLabel The label that shows up on the button.
|
||||||
|
* @deprecated Refactor to use setButtonLabel(GlobalizedMessage) instead
|
||||||
|
*/
|
||||||
|
public void setButtonLabel(String buttonLabel) {
|
||||||
|
setButtonLabel(new GlobalizedMessage(buttonLabel));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Sets the text that will be displayed on the actual button.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param buttonLabel The label that shows up on the button.
|
||||||
|
*/
|
||||||
|
public void setButtonLabel(GlobalizedMessage buttonLabel) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_buttonLabel = buttonLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the submit button. Submit buttons are special,
|
||||||
|
* since only the value for the submit button that is clicked is
|
||||||
|
* submitted by the browser. Contrary to what <code>getValue</code>
|
||||||
|
* does for other widgets, the value returned from this incarnation
|
||||||
|
* of <code>getValue</code> is either the value that was included in
|
||||||
|
* the current request, or, if there is none, the default value.
|
||||||
|
*
|
||||||
|
* Must not be final, because globalized Submit needs to override.
|
||||||
|
*/
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
return getValue().localize(ps.getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Return the the buttons label.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return GlobalizedMessage The buttons label.
|
||||||
|
*/
|
||||||
|
public GlobalizedMessage getValue() {
|
||||||
|
return m_buttonLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generates the DOM for this widget
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param state The current PageState.
|
||||||
|
* @param parent This widget's parent.
|
||||||
|
*/
|
||||||
|
protected void generateWidget(PageState state, Element parent) {
|
||||||
|
Element widget = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
|
||||||
|
widget.addAttribute("type", getType());
|
||||||
|
widget.addAttribute("name", getName());
|
||||||
|
exportAttributes(widget);
|
||||||
|
if (getValue(state) != null) {
|
||||||
|
widget.addAttribute("value", (String) getValue(state));
|
||||||
|
} else {
|
||||||
|
widget.addAttribute("value", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return <code>true</code> if the user clicked on this submit button to
|
||||||
|
* submit the form in which the button is contained.
|
||||||
|
*
|
||||||
|
* @param state the state of the current request
|
||||||
|
* @return <code>true</code> if the user clicked this button to submit
|
||||||
|
* the enclosing form.
|
||||||
|
*/
|
||||||
|
public boolean isSelected(PageState ps) {
|
||||||
|
ParameterData p = getParameterData(ps);
|
||||||
|
|
||||||
|
return (ps != null) && (p.getValue() != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTML size attribute of this widget.
|
||||||
|
*
|
||||||
|
* @param n The size of this widget, in characters
|
||||||
|
*/
|
||||||
|
public void setSize(int n) {
|
||||||
|
setAttribute("size", Integer.toString(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the onclick parameter and <em>disables the Javascript-based double-click protection</em>
|
||||||
|
* for this widget.
|
||||||
|
* This is the preferred method to disable double-click protection on per-widget basis.
|
||||||
|
* @param command The JavaScript to execute when the button is clicked.
|
||||||
|
*/
|
||||||
|
public void setOnClick(String command) {
|
||||||
|
setAttribute(ON_CLICK, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the onclick parameter.
|
||||||
|
*/
|
||||||
|
public String getOnClick() {
|
||||||
|
return getAttribute(ON_CLICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers getName()+".x" and getName()+".y" so that html image
|
||||||
|
* submits will work too
|
||||||
|
*/
|
||||||
|
public void register(Form f, FormModel m) {
|
||||||
|
super.register(f,m);
|
||||||
|
m.addFormParam(new StringParameter(getParameterModel().getName() + ".x"));
|
||||||
|
m.addFormParam(new StringParameter(getParameterModel().getName() + ".y"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the parameter value for this widget
|
||||||
|
* @post returns null if the FormData are missing
|
||||||
|
*/
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
ParameterData data = super.getParameterData(ps);
|
||||||
|
if (data != null && data.getValue() != null) {
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
FormData fd = getForm().getFormData(ps);
|
||||||
|
if (fd != null) {
|
||||||
|
return fd.getParameter(getName()+".x");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
// This interface contains the XML element name of this class
|
||||||
|
// in a constant which is used when generating XML
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a textarea field in an HTML form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class TextArea extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>OFF</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. See <a
|
||||||
|
* href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int OFF = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>HARD</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. * See <a
|
||||||
|
* //href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int HARD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for specifying <tt>SOFT</tt> value for the
|
||||||
|
* <tt>WRAP</tt> attribute of this image input. See <a
|
||||||
|
* href="http://developer.netscape.com/docs/manuals/htmlguid/tags10.htm#1340340">here</a>
|
||||||
|
* for a description of what this attribute does.
|
||||||
|
*/
|
||||||
|
public static final int SOFT = 2;
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
// * * * Fields * * *
|
||||||
|
// -------------------------------------
|
||||||
|
// -------------------------------------
|
||||||
|
// * * * Methods * * *
|
||||||
|
// -------------------------------------
|
||||||
|
public TextArea(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextArea(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience constructor
|
||||||
|
*/
|
||||||
|
public TextArea(String name, int rows, int cols, int wrap) {
|
||||||
|
super(name);
|
||||||
|
setRows(rows);
|
||||||
|
setCols(cols);
|
||||||
|
setWrap(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience constructor
|
||||||
|
*/
|
||||||
|
public TextArea(ParameterModel model, int rows, int cols, int wrap) {
|
||||||
|
super(model);
|
||||||
|
setRows(rows);
|
||||||
|
setCols(cols);
|
||||||
|
setWrap(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "textarea";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ROWS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setRows(int rows) {
|
||||||
|
setAttribute("rows", String.valueOf(rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>COLS</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setCols(int cols) {
|
||||||
|
setAttribute("cols", String.valueOf(cols));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>WRAP</tt> attribute for the <tt>TEXTAREA</tt> tag.
|
||||||
|
*/
|
||||||
|
public void setWrap(int wrap) {
|
||||||
|
String wrapString = null;
|
||||||
|
|
||||||
|
switch (wrap) {
|
||||||
|
case OFF:
|
||||||
|
wrapString = "off";
|
||||||
|
break;
|
||||||
|
case HARD:
|
||||||
|
wrapString = "hard";
|
||||||
|
break;
|
||||||
|
case SOFT:
|
||||||
|
wrapString = "soft";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapString != null) {
|
||||||
|
setAttribute("wrap", wrapString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default value (text)
|
||||||
|
*
|
||||||
|
* @deprecated [since 17Aug2001] use {@link Widget#setDefaultValue(Object)}
|
||||||
|
*/
|
||||||
|
public void setValue(String text) {
|
||||||
|
this.setDefaultValue(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a compound widget?
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML tag.
|
||||||
|
*
|
||||||
|
* @return The tag to be used for the top level DOM element generated for this type of Widget.
|
||||||
|
*/
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_TEXTAREA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the DOM for the textarea widget.
|
||||||
|
* <p>
|
||||||
|
* Generates DOM fragment:
|
||||||
|
* <p>
|
||||||
|
* <code><bebop:textarea name=... value=... [onXXX=...]/>
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
public void generateWidget(PageState state, Element parent) {
|
||||||
|
Element textarea = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
|
||||||
|
textarea.addAttribute("name", getName());
|
||||||
|
generateDescriptionXML(state, textarea);
|
||||||
|
ParameterData pData = getParameterData(state);
|
||||||
|
if (null != pData) {
|
||||||
|
String value = pData.marshal();
|
||||||
|
if (value == null) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
textarea.addAttribute("value", value);
|
||||||
|
}
|
||||||
|
exportAttributes(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormSection;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.GridPanel;
|
||||||
|
import com.arsdigita.util.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form section that takes a text entry component (e.g., TextField)
|
||||||
|
* and displays it along with a drop-down box that enables the user to
|
||||||
|
* select the input type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TextEntryFormSection extends FormSection
|
||||||
|
implements MessageType {
|
||||||
|
|
||||||
|
private Widget m_widget;
|
||||||
|
private OptionGroup m_textType = null;
|
||||||
|
|
||||||
|
public TextEntryFormSection(Widget w) {
|
||||||
|
super(new GridPanel(2));
|
||||||
|
m_widget = w;
|
||||||
|
add(m_widget);
|
||||||
|
|
||||||
|
if (w.getParameterModel().getValueClass().equals(String.class)) {
|
||||||
|
m_textType = new SingleSelect(w.getName() + ".textType");
|
||||||
|
m_textType.addOption(new Option(MessageType.TEXT_PLAIN, "Plain text"));
|
||||||
|
m_textType.addOption(new Option(MessageType.TEXT_HTML, "HTML"));
|
||||||
|
m_textType.addOption(new Option(MessageType.TEXT_PREFORMATTED, "Preformatted text"));
|
||||||
|
add(m_textType);
|
||||||
|
} else {
|
||||||
|
m_textType = new SingleSelect(w.getName() + ".textType") {
|
||||||
|
public boolean isVisible(PageState ps) {
|
||||||
|
return false;
|
||||||
|
}};
|
||||||
|
add(m_textType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidgetValue(PageState ps, Object value) {
|
||||||
|
m_widget.setValue(ps, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getWidgetValue(PageState ps) {
|
||||||
|
return m_widget.getValue(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTextType(PageState ps) {
|
||||||
|
// Null if it's a Deditor
|
||||||
|
if ( m_textType == null ) {
|
||||||
|
return MessageType.TEXT_HTML;
|
||||||
|
} else {
|
||||||
|
return (String) m_textType.getValue(ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextType(PageState ps, String type) {
|
||||||
|
m_textType.setValue(ps, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a text field in an HTML form.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class TextField extends Widget {
|
||||||
|
|
||||||
|
public TextField(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextField(ParameterModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget.
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>MAXLENGTH</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setMaxLength(int length) {
|
||||||
|
setAttribute("MAXLENGTH", String.valueOf(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>SIZE</tt> attribute for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setSize(int size) {
|
||||||
|
setAttribute("SIZE", String.valueOf(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.parameters.DateTimeParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.NotNullValidationListener;
|
||||||
|
import com.arsdigita.bebop.parameters.NumberInRangeValidationListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.TimeParameter;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.globalization.GlobalizationHelper;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
|
||||||
|
import java.text.DateFormatSymbols;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a time field in an HTML form.
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.bebop.form.DateTime
|
||||||
|
* @author Dave Turner
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Time extends Widget implements BebopConstants {
|
||||||
|
|
||||||
|
private TextField m_hour;
|
||||||
|
private TextField m_minute;
|
||||||
|
private TextField m_second;
|
||||||
|
private OptionGroup m_amOrPm;
|
||||||
|
private boolean m_showSeconds;
|
||||||
|
private static final String ZERO = "0";
|
||||||
|
|
||||||
|
private class HourFragment extends TextField {
|
||||||
|
|
||||||
|
private Time parent;
|
||||||
|
|
||||||
|
public HourFragment(String name, Time parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
if (has12HourClock()) {
|
||||||
|
this.addValidationListener(new NumberInRangeValidationListener(1, 12));
|
||||||
|
} else {
|
||||||
|
this.addValidationListener(new NumberInRangeValidationListener(1, 24));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
if (has12HourClock()) {
|
||||||
|
return parent.getFragmentValue(ps, Calendar.HOUR);
|
||||||
|
} else {
|
||||||
|
return parent.getFragmentValue(ps, Calendar.HOUR_OF_DAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MinuteFragment extends TextField {
|
||||||
|
|
||||||
|
private Time parent;
|
||||||
|
|
||||||
|
public MinuteFragment(String name, Time parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
this.addValidationListener(new NumberInRangeValidationListener(0, 59));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
Integer min = (Integer) parent.getFragmentValue(ps, Calendar.MINUTE);
|
||||||
|
if (min == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (min.intValue() < 10) {
|
||||||
|
return ZERO + min.toString();
|
||||||
|
} else {
|
||||||
|
return min.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SecondFragment extends TextField {
|
||||||
|
|
||||||
|
private Time parent;
|
||||||
|
|
||||||
|
public SecondFragment(String name, Time parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
this.addValidationListener(new NumberInRangeValidationListener(0, 59));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
Integer sec = (Integer) parent.getFragmentValue(ps, Calendar.SECOND);
|
||||||
|
if (sec == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (sec.intValue() < 10) {
|
||||||
|
return ZERO + sec.toString();
|
||||||
|
} else {
|
||||||
|
return sec.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AmPmFragment extends SingleSelect {
|
||||||
|
|
||||||
|
private Time parent;
|
||||||
|
|
||||||
|
public AmPmFragment(String name, Time parent) {
|
||||||
|
super(name);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Object value = getValue(ps);
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ParameterData(getParameterModel(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
return parent.getFragmentValue(ps, Calendar.AM_PM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor. */
|
||||||
|
public Time(ParameterModel model) {
|
||||||
|
this(model, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor. */
|
||||||
|
public Time(ParameterModel model, boolean showSeconds) {
|
||||||
|
super(model);
|
||||||
|
|
||||||
|
if (!(model instanceof TimeParameter || model instanceof DateTimeParameter)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The Time widget " + model.getName()
|
||||||
|
+ " must be backed by a TimeParameter parameter model");
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = model.getName();
|
||||||
|
String nameHour = name + ".hour";
|
||||||
|
String nameMinute = name + ".minute";
|
||||||
|
String nameSecond = name + ".second";
|
||||||
|
String nameAmOrPm = name + ".amOrPm";
|
||||||
|
|
||||||
|
DateFormatSymbols dfs = new DateFormatSymbols();
|
||||||
|
|
||||||
|
m_hour = new HourFragment(nameHour, this);
|
||||||
|
m_minute = new MinuteFragment(nameMinute, this);
|
||||||
|
m_showSeconds = showSeconds;
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second = new SecondFragment(nameSecond, this);
|
||||||
|
} else {
|
||||||
|
m_second = null;
|
||||||
|
}
|
||||||
|
m_amOrPm = new AmPmFragment(nameAmOrPm, this);
|
||||||
|
|
||||||
|
m_hour.setMaxLength(2);
|
||||||
|
m_hour.setSize(2);
|
||||||
|
m_minute.setMaxLength(2);
|
||||||
|
m_minute.setSize(2);
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second.setMaxLength(2);
|
||||||
|
m_second.setSize(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] amPmStrings = dfs.getAmPmStrings();
|
||||||
|
for (int i = 0; i < amPmStrings.length; i++) {
|
||||||
|
m_amOrPm.addOption(new Option(String.valueOf(i), amPmStrings[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Time(String name) {
|
||||||
|
this(new TimeParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a string naming the type of this widget. */
|
||||||
|
public String getType() {
|
||||||
|
return "time";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>MAXLENGTH</tt> attributes for the <tt>INPUT</tt> tag
|
||||||
|
* used to render this form element.
|
||||||
|
*/
|
||||||
|
public void setMaxLength(int length) {
|
||||||
|
setAttribute("MAXLENGTH", String.valueOf(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompound() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The XML tag for this derived class of Widget. */
|
||||||
|
@Override
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateWidget(PageState ps, Element parent) {
|
||||||
|
|
||||||
|
if (!isVisible(ps)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element time = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
time.addAttribute("name", getParameterModel().getName());
|
||||||
|
generateDescriptionXML(ps, time);
|
||||||
|
generateLocalizedWidget(ps, time);
|
||||||
|
|
||||||
|
// If Element could be null insert a extra widget to clear entry
|
||||||
|
if (!hasValidationListener(new NotNullValidationListener())) {
|
||||||
|
time.newChildElement("NoTime");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateLocalizedWidget(PageState ps, Element time) {
|
||||||
|
m_hour.generateXML(ps, time);
|
||||||
|
m_minute.generateXML(ps, time);
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second.generateXML(ps, time);
|
||||||
|
}
|
||||||
|
if (has12HourClock()) {
|
||||||
|
m_amOrPm.generateXML(ps, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisabled() {
|
||||||
|
m_hour.setDisabled();
|
||||||
|
m_minute.setDisabled();
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second.setDisabled();
|
||||||
|
}
|
||||||
|
if (has12HourClock()) {
|
||||||
|
m_amOrPm.setDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly() {
|
||||||
|
m_hour.setReadOnly();
|
||||||
|
m_minute.setReadOnly();
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second.setReadOnly();
|
||||||
|
}
|
||||||
|
if (has12HourClock()) {
|
||||||
|
m_amOrPm.setReadOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Form Object for this Widget. This method will throw an
|
||||||
|
* exception if the _form pointer is already set. To explicity
|
||||||
|
* change the _form pointer the developer must first call
|
||||||
|
* setForm(null)
|
||||||
|
*
|
||||||
|
* @param the <code>Form</code> Object for this Widget.
|
||||||
|
* @exception IllegalStateException if form already set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setForm(Form f) {
|
||||||
|
super.setForm(f);
|
||||||
|
m_hour.setForm(f);
|
||||||
|
m_minute.setForm(f);
|
||||||
|
if (m_showSeconds) {
|
||||||
|
m_second.setForm(f);
|
||||||
|
}
|
||||||
|
m_amOrPm.setForm(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFragmentValue(PageState ps, int field) {
|
||||||
|
Assert.exists(ps, "PageState");
|
||||||
|
FormData f = getForm().getFormData(ps);
|
||||||
|
if (f != null) {
|
||||||
|
java.util.Date value = (java.util.Date) f.get(getName());
|
||||||
|
if (value != null) {
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
c.setTime(value);
|
||||||
|
int intVal = c.get(field);
|
||||||
|
if (field == Calendar.HOUR && intVal == 0 && has12HourClock()) {
|
||||||
|
intVal = 12;
|
||||||
|
}
|
||||||
|
return new Integer(intVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean has12HourClock() {
|
||||||
|
Locale locale = GlobalizationHelper.getNegotiatedLocale();
|
||||||
|
DateFormat format_12Hour = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.US);
|
||||||
|
DateFormat format_locale = DateFormat.getTimeInstance(DateFormat.SHORT, locale);
|
||||||
|
|
||||||
|
String midnight = "";
|
||||||
|
try {
|
||||||
|
midnight = format_locale.format(format_12Hour.parse("12:00 AM"));
|
||||||
|
} catch (ParseException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return midnight.contains("12");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,793 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.form;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.TooManyListenersException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.DescriptiveComponent;
|
||||||
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
|
import com.arsdigita.bebop.FormModel;
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.event.EventListenerList;
|
||||||
|
import com.arsdigita.bebop.event.ParameterEvent;
|
||||||
|
import com.arsdigita.bebop.event.ParameterListener;
|
||||||
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterModel;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.bebop.util.BebopConstants;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
// import com.arsdigita.kernel.Kernel;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A class representing a widget in the graphical representation of a form.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A widget may correspond to a standard HTML form element, or to a more
|
||||||
|
* specific element or set of elements, such as a date widget that allows
|
||||||
|
* input of month, day and year (and possibly time as well).</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class and its subclasses provide methods to set all element attributes
|
||||||
|
* except for <code>VALUE</code>, which is typically dependent on the request.
|
||||||
|
* At the time of a request, a widget object merges a dynamically specified
|
||||||
|
* value or set of values with its own set of persistent attributes to render
|
||||||
|
* the final HTML for the widget. Other dynamic attributes may be associated with
|
||||||
|
* the form component via a <code>WidgetPeer</code> associated with the widget.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The parent class provides the Label (the localized title) for the widget as
|
||||||
|
* well as a (localized) hint as a kind of online manual for the user.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class Widget extends DescriptiveComponent
|
||||||
|
implements Cloneable, BebopConstants {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(Widget.class);
|
||||||
|
|
||||||
|
private ParameterModel m_parameterModel;
|
||||||
|
private final EventListenerList m_listeners = new EventListenerList();
|
||||||
|
private ParameterListener m_forwardParameter = null;
|
||||||
|
private PrintListener m_printListener;
|
||||||
|
private Form m_form;
|
||||||
|
|
||||||
|
private ValidationGuard m_guard = null;
|
||||||
|
|
||||||
|
// This controls whether or not validation listeners are fired when the
|
||||||
|
// widget is not visible. By default this is true.
|
||||||
|
private boolean m_validateInvisible = true;
|
||||||
|
|
||||||
|
static final String ON_FOCUS = "onFocus";
|
||||||
|
static final String ON_BLUR = "onBlur";
|
||||||
|
static final String ON_SELECT = "onSelect";
|
||||||
|
static final String ON_CHANGE = "onChange";
|
||||||
|
static final String ON_KEY_UP = "onKeyUp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, creates a new widget.
|
||||||
|
*
|
||||||
|
* @param name of the widget, used to address the instance.
|
||||||
|
*/
|
||||||
|
protected Widget(String name) {
|
||||||
|
this(new StringParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, creates a new widget.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Each new widget is associated with a ParameterModel describing the
|
||||||
|
* data object(s) submitted
|
||||||
|
* from the widget.
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
protected Widget(ParameterModel model) {
|
||||||
|
|
||||||
|
Assert.exists(model, ParameterModel.class);
|
||||||
|
m_parameterModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the widget consists of multiple HTML elements.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract boolean isCompound();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string naming the type of this widget. Must be implemented by subclasses!
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract String getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected ParameterListener createParameterListener() {
|
||||||
|
return new ParameterListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(ParameterEvent evt)
|
||||||
|
throws FormProcessException {
|
||||||
|
fireValidation(new ParameterEvent(Widget.this, evt.getParameterData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidateInvisible(boolean value) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_validateInvisible = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateInvisible() {
|
||||||
|
return m_validateInvisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireValidation(ParameterEvent evt)
|
||||||
|
throws FormProcessException {
|
||||||
|
Assert.isLocked(this);
|
||||||
|
|
||||||
|
PageState ps = evt.getPageState();
|
||||||
|
|
||||||
|
if ((!validateInvisible() && !ps.isVisibleOnPage(this))
|
||||||
|
|| ((m_guard != null) && m_guard.shouldValidate(ps))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Iterator it
|
||||||
|
= m_listeners.getListenerIterator(ParameterListener.class);
|
||||||
|
it.hasNext();) {
|
||||||
|
((ParameterListener) it.next()).validate(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValidationListener(ParameterListener listener) {
|
||||||
|
Assert.exists(listener, "ParameterListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
if (m_forwardParameter == null) {
|
||||||
|
m_forwardParameter = createParameterListener();
|
||||||
|
m_parameterModel.addParameterListener(m_forwardParameter);
|
||||||
|
}
|
||||||
|
m_listeners.add(ParameterListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeValidationListener(ParameterListener listener) {
|
||||||
|
Assert.exists(listener, "ParameterListener");
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_listeners.remove(ParameterListener.class, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for existens of a particular type of ValidationListener
|
||||||
|
*
|
||||||
|
* @param listener Subtype of ParameterListern which is tested for
|
||||||
|
*
|
||||||
|
* @return true if subtype is in the list
|
||||||
|
*/
|
||||||
|
public boolean hasValidationListener(ParameterListener listener) {
|
||||||
|
Assert.exists(listener, "ParameterListener");
|
||||||
|
return this.getParameterModel().hasParameterListener(listener);
|
||||||
|
|
||||||
|
// return (m_listeners.getListenerCount(listener.getClass()) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a print listener for this widget. Only one print listener can be set for a widget, since
|
||||||
|
* the <code>PrintListener</code> is expected to modify the target of the
|
||||||
|
* <code>PrintEvent</code>.
|
||||||
|
*
|
||||||
|
* @param listener the print listener
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException <code>listener</code> is null
|
||||||
|
* @throws TooManyListenersException a print listener has previously been added
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void addPrintListener(PrintListener listener)
|
||||||
|
throws TooManyListenersException, IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("Argument listener can not be null");
|
||||||
|
}
|
||||||
|
if (m_printListener != null) {
|
||||||
|
throw new TooManyListenersException();
|
||||||
|
}
|
||||||
|
m_printListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the print listener for this widget. Since there can only be one print listener for a
|
||||||
|
* widget, this lets you just set it and avoid writing a try/catch block for
|
||||||
|
* "TooManyListenersException". Any existing listener will be overwritten.
|
||||||
|
*
|
||||||
|
* @param listener the print listener
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException <code>listener</code> is null
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void setPrintListener(PrintListener listener)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("Argument listener can not be null");
|
||||||
|
}
|
||||||
|
m_printListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a previously added print listener. If <code>listener</code> is not the listener that
|
||||||
|
* has been added with {@link #addPrintListener addPrintListener}, an IllegalArgumentException
|
||||||
|
* will be thrown.
|
||||||
|
*
|
||||||
|
* @param listener the listener that had been added with <code>addPrintListener</code>
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException <code>listener</code> is not the currently registered print
|
||||||
|
* listener or is <code>null</code>.
|
||||||
|
* @pre listener != null
|
||||||
|
*/
|
||||||
|
public void removePrintListener(PrintListener listener)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
if (listener == null) {
|
||||||
|
throw new IllegalArgumentException("listener can not be null");
|
||||||
|
}
|
||||||
|
if (listener != m_printListener) {
|
||||||
|
throw new IllegalArgumentException("listener is not registered with this widget");
|
||||||
|
}
|
||||||
|
m_printListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the ParameterModel of this Widget with the containing Form. This method is used by
|
||||||
|
* the Bebop framework and should not be used by application developers.
|
||||||
|
*
|
||||||
|
* @param form
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void register(Form form, FormModel model) {
|
||||||
|
model.addFormParam(getParameterModel());
|
||||||
|
|
||||||
|
setForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Form Object for this Widget. This method will throw an exception if the _form
|
||||||
|
* pointer is already set. To explicity change the m_form pointer the developer must first call
|
||||||
|
* setForm(null)
|
||||||
|
*
|
||||||
|
* @param form The <code>Form</code> Object for this Widget
|
||||||
|
*
|
||||||
|
* @exception IllegalStateException if form already set.
|
||||||
|
*/
|
||||||
|
public void setForm(final Form form) {
|
||||||
|
if (m_form != null && form != null) {
|
||||||
|
throw new IllegalStateException("Form " + form.getName()
|
||||||
|
+ " already set for "
|
||||||
|
+ getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Form Object for this Widget. Throws an exception if the Widget doesn't belong to a
|
||||||
|
* form.
|
||||||
|
*
|
||||||
|
* @return the {@link Form} Object for this Widget.
|
||||||
|
*
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public Form getForm() throws RuntimeException {
|
||||||
|
if (m_form == null) {
|
||||||
|
throw new RuntimeException("Widget " + this + " (" + getName() + ") "
|
||||||
|
+ "isn't associated with any Form");
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONFOCUS</tt> attribute for the HTML tags that compose this element.
|
||||||
|
*
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnFocus(String javascriptCode) {
|
||||||
|
setAttribute(ON_FOCUS, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONBLUR</tt> attribute for the HTML tags that compose this element.
|
||||||
|
*
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnBlur(String javascriptCode) {
|
||||||
|
setAttribute(ON_BLUR, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONSELECT</tt> attribute for the HTML tags that compose this element.
|
||||||
|
*
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnSelect(String javascriptCode) {
|
||||||
|
setAttribute(ON_SELECT, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ONCHANGE</tt> attribute for the HTML tags that compose this element.
|
||||||
|
*
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnChange(String javascriptCode) {
|
||||||
|
setAttribute(ON_CHANGE, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>ON_KEY_UP</tt> attribute for the HTML tags that compose this element.
|
||||||
|
*
|
||||||
|
* @param javascriptCode
|
||||||
|
*/
|
||||||
|
public void setOnKeyUp(String javascriptCode) {
|
||||||
|
setAttribute(ON_KEY_UP, javascriptCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default value in the parameter model for this element. This is a static property and
|
||||||
|
* this method should not be invoked at request time (not even in a PrintListener).
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void setDefaultValue(Object value) {
|
||||||
|
m_parameterModel.setDefaultValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this widget as readonly, which has the effect of preventing the user from modifying the
|
||||||
|
* widget's contents. This method can only be called on unlocked widgets.
|
||||||
|
*/
|
||||||
|
public void setReadOnly() {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("readonly", "readonly");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this widget as disabled, which has the effect of preventing the widget's value being
|
||||||
|
* submitted with the form, and will typically cause the widget to be 'grayed out' on the form.
|
||||||
|
* This method can only be called on unlocked widgets.
|
||||||
|
*/
|
||||||
|
public void setDisabled() {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("disabled", "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a popup hint for the widget.
|
||||||
|
*
|
||||||
|
* @param hint
|
||||||
|
*
|
||||||
|
* @deprecated refactor to use a GlobalizedMessage instead and use setHint(GlobalizedMessage
|
||||||
|
* hint)
|
||||||
|
*/
|
||||||
|
public void setHint(String hint) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
setAttribute("hint", hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a popup hint for the widget.
|
||||||
|
*
|
||||||
|
* @param hint
|
||||||
|
*/
|
||||||
|
// Use parent's class method instead
|
||||||
|
// @Override
|
||||||
|
// public void setHint(GlobalizedMessage hint) {
|
||||||
|
// Assert.isUnlocked(this);
|
||||||
|
// setAttribute("hint", (String) hint.localize());
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Sets a Label for the widget.
|
||||||
|
*
|
||||||
|
* @param label
|
||||||
|
*/
|
||||||
|
// Use parent's class method instead
|
||||||
|
// public void setLabel(GlobalizedMessage label) {
|
||||||
|
// m_label = label;
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Sets a Label for the widget.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
// Use parent's class method instead
|
||||||
|
// public GlobalizedMessage getLabel() {
|
||||||
|
// return m_label;
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Gets the default value in the parameter model for this element.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getDefaultValue() {
|
||||||
|
Object o = m_parameterModel.getDefaultValue();
|
||||||
|
if (o == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return m_parameterModel.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "pass in" property determines whether the value for this parameter is generally passed in
|
||||||
|
* from the outside. If this property is <code>true</code>, the model always tries to get the
|
||||||
|
* parameter value from the request, no matter whether the request is the initial request or a
|
||||||
|
* submission of the form to which the widget belongs.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If this property is <code>false</code>, the parameter value is only read from the request if
|
||||||
|
* it is a submission of the form containing the widget.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By default, this property is <code>false</code>.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if an attempt should always be made to retrieve the parameter value
|
||||||
|
* from the request.
|
||||||
|
*/
|
||||||
|
public final boolean isPassIn() {
|
||||||
|
return getParameterModel().isPassIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this parameter should be treated as a "pass in" parameter. This is a static
|
||||||
|
* property of the ParameterModel and this method should not be invoked at request-time.
|
||||||
|
*
|
||||||
|
* @see #isPassIn
|
||||||
|
* @param v <code>true</code> if this parameter is a pass in parameter.
|
||||||
|
*/
|
||||||
|
public final void setPassIn(boolean v) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
getParameterModel().setPassIn(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ParameterModel is normally set via the constructors. This method is only rarely needed.
|
||||||
|
* Please note that the previous ParameterModel and all its listeners will be lost.
|
||||||
|
*
|
||||||
|
* @param parameterModel
|
||||||
|
*/
|
||||||
|
public final void setParameterModel(ParameterModel parameterModel) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_parameterModel = parameterModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows access to underlying parameterModel. The ParameterModel contains static
|
||||||
|
* (request-independent) properties of a Widget such as its name, default value and its
|
||||||
|
* listeners. The ParameterModel can not be modified once Page.lock() has been invoked (not even
|
||||||
|
* in a PrintListener). This is done after the Page has been built, normally at server startup.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final ParameterModel getParameterModel() {
|
||||||
|
return m_parameterModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This method creates the DOM for the widget. The method is called by the Bebop framework and
|
||||||
|
* should not be invoked by application developers.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The method first fires the print event allowing application developers to set certain
|
||||||
|
* properties of the Widget at request time in a PrintListener. The methods generateWidget and
|
||||||
|
* generateErrors will then be invoked to generate either of the following
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <code><bebop:formErrors message=...</bebop:formErrors></code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <code>
|
||||||
|
* <bebop:formWidget name=... type=... label=... value=...
|
||||||
|
* [hint=...] [onXXX=...] </bebop:formWidget>
|
||||||
|
* </code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(final PageState state, final Element parent) {
|
||||||
|
|
||||||
|
if (isVisible(state)) {
|
||||||
|
Widget w = firePrintEvent(state);
|
||||||
|
|
||||||
|
w.generateWidget(state, parent);
|
||||||
|
w.generateErrors(state, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Widget firePrintEvent(PageState state) {
|
||||||
|
Widget w = this;
|
||||||
|
if (m_printListener != null) {
|
||||||
|
try {
|
||||||
|
w = (Widget) this.clone();
|
||||||
|
w.setForm(m_form);
|
||||||
|
|
||||||
|
m_printListener.prepare(new PrintEvent(this, state, w));
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
// FIXME: Failing silently here isn't so great
|
||||||
|
// It probably indicates a serious programming error
|
||||||
|
w = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML tag.
|
||||||
|
*
|
||||||
|
* @return The tag to be used for the top level DOM element generated for this type of Widget.
|
||||||
|
*/
|
||||||
|
protected String getElementTag() {
|
||||||
|
return BEBOP_FORMWIDGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the DOM for the given widget on a per request basis.
|
||||||
|
* <p>
|
||||||
|
* Generates DOM fragment:
|
||||||
|
* <p>
|
||||||
|
* <code><bebop:formWidget name=... type=... value=... [onXXX=...]>
|
||||||
|
* </bebop:formWidget></code>
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
protected void generateWidget(PageState state, Element parent) {
|
||||||
|
|
||||||
|
Element widget = parent.newChildElement(getElementTag(), BEBOP_XML_NS);
|
||||||
|
|
||||||
|
widget.addAttribute("type", getType());
|
||||||
|
widget.addAttribute("name", getName());
|
||||||
|
widget.addAttribute("class", getName().replace(".", " "));
|
||||||
|
generateDescriptionXML(state, widget);
|
||||||
|
// if (m_label != null) {
|
||||||
|
// widget.addAttribute("label",
|
||||||
|
// (String) m_label.localize(state.getRequest()));
|
||||||
|
// }
|
||||||
|
exportAttributes(widget);
|
||||||
|
String value = null;
|
||||||
|
ParameterData p = getParameterData(state);
|
||||||
|
if (p != null) {
|
||||||
|
value = p.marshal();
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
widget.addAttribute("value", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML for the given widget.
|
||||||
|
* <p>
|
||||||
|
* Generates XML fragment: <code><bebop:formErrors message=... id=name>
|
||||||
|
* </bebop:formErrors></code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
protected void generateErrors(PageState state, Element parent) {
|
||||||
|
Iterator i = getErrors(state);
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
Element errors = parent.newChildElement(BEBOP_FORMERRORS, BEBOP_XML_NS);
|
||||||
|
errors.addAttribute("message",
|
||||||
|
(String) ((GlobalizedMessage) i.next()).localize(state.getRequest())
|
||||||
|
);
|
||||||
|
errors.addAttribute("id", getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value associated with this widget in the request described by <code>ps</code>. The
|
||||||
|
* type of the returned object depends on the <code>ParameterModel</code> underlying this
|
||||||
|
* widget. This method is typically called in a FormProcessListener to access the value that was
|
||||||
|
* submitted for a Widget in the Form.
|
||||||
|
*
|
||||||
|
* @param ps describes the request currently being processed
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @pre ps != null
|
||||||
|
* @post may return null
|
||||||
|
*/
|
||||||
|
public Object getValue(PageState ps) {
|
||||||
|
Assert.exists(ps);
|
||||||
|
Object data = null;
|
||||||
|
ParameterData p = getParameterData(ps);
|
||||||
|
if (p == null || p.getValue() == null) {
|
||||||
|
// check if value is in session
|
||||||
|
HttpSession session = ps.getRequest().getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
data = session.getAttribute(getName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = p.getValue();
|
||||||
|
}
|
||||||
|
return (data == null) ? getDefaultValue() : data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of the parameter associated with this widget to a new value. The exact type of
|
||||||
|
* <code>value</code> depends on the <code>ParameterModel</code> underlying the widget. This
|
||||||
|
* method is typically called in a FormInitListener to initialize the value of a Widget in the
|
||||||
|
* Form at request time.
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
* @param value
|
||||||
|
*
|
||||||
|
* @pre ps != null
|
||||||
|
* @post value == getValue(ps)
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException the form to which the widget belongs has not been processed
|
||||||
|
* yet.
|
||||||
|
*/
|
||||||
|
public void setValue(PageState ps, Object value)
|
||||||
|
throws IllegalStateException {
|
||||||
|
Assert.exists(ps, "PageState");
|
||||||
|
ParameterData p = getParameterData(ps);
|
||||||
|
// set value in session if it is being held - allows
|
||||||
|
// updates in wizard forms where init is not called each
|
||||||
|
// step
|
||||||
|
HttpSession session = ps.getRequest().getSession(false);
|
||||||
|
if (session != null && session.getAttribute(getName()) != null) {
|
||||||
|
session.setAttribute(getName(), value);
|
||||||
|
}
|
||||||
|
if (p != null) {
|
||||||
|
p.setValue(value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Cannot set value for widget '" + getName()
|
||||||
|
+ "': corresponding form '"
|
||||||
|
+ getForm().getName()
|
||||||
|
+ "' has not been processed yet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Iterator getErrors(PageState ps) {
|
||||||
|
Assert.exists(ps, "PageState");
|
||||||
|
FormData f = getForm().getFormData(ps);
|
||||||
|
if (f != null) {
|
||||||
|
return f.getErrors(getName());
|
||||||
|
}
|
||||||
|
return Collections.EMPTY_LIST.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ps
|
||||||
|
*
|
||||||
|
* @return the parameter value for this widget
|
||||||
|
*
|
||||||
|
* @post returns null if the FormData are missing
|
||||||
|
*/
|
||||||
|
protected ParameterData getParameterData(PageState ps) {
|
||||||
|
Assert.exists(ps, "PageState");
|
||||||
|
FormData fd = getForm().getFormData(ps);
|
||||||
|
if (fd != null) {
|
||||||
|
return fd.getParameter(getName());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to an incoming request by calling <code>respond</code> on the form to which the
|
||||||
|
* widget belongs. This method is called by the Bebop framework and should not be invoked by
|
||||||
|
* application developers. It is somewhat questionable that this method should ever be called,
|
||||||
|
* rather than having {@link Form#respond Form.respond()} called directly.
|
||||||
|
*
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
* @pre state != null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void respond(PageState state) throws javax.servlet.ServletException {
|
||||||
|
getForm().respond(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone() throws CloneNotSupportedException {
|
||||||
|
Widget cloned = (Widget) super.clone();
|
||||||
|
cloned.setForm(null);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a Widget. ValidationGuard implementation to use to determine if this widget should
|
||||||
|
* run its validation listeners.
|
||||||
|
*
|
||||||
|
* @param guard the Widget.ValidationGuard.
|
||||||
|
*/
|
||||||
|
public void setValidationGuard(ValidationGuard guard) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_guard = guard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner interface used to determine if the validation listeners should be run for this widget
|
||||||
|
* or not.
|
||||||
|
*/
|
||||||
|
public interface ValidationGuard {
|
||||||
|
|
||||||
|
boolean shouldValidate(PageState ps);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error to be displayed with this parameter.
|
||||||
|
*
|
||||||
|
* @param msg A GlobalizedMessage that will resolve to the error for the user.
|
||||||
|
*/
|
||||||
|
public void addError(GlobalizedMessage msg) {
|
||||||
|
PageState state = PageState.getPageState();
|
||||||
|
getParameterData(state).addError(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error to be displayed with this parameter.
|
||||||
|
*
|
||||||
|
* @param error A string showing the error to the user.
|
||||||
|
*
|
||||||
|
* @deprecated refactor to use addError(GlobalizedMessage) instead.
|
||||||
|
*/
|
||||||
|
public void addError(String error) {
|
||||||
|
getParameterData(PageState.getPageState()).addError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " [" + getName() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.list;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.List;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.util.LockableImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class that implements ListModelBuilder by concretely
|
||||||
|
* implementing the Lockable interface and leaving only the makeModel method
|
||||||
|
* abstract. This allows for anonymous inner classes that implement the
|
||||||
|
* ListModelBuilder interface without having to implement Lockable as well.
|
||||||
|
*
|
||||||
|
* @author Archit Shah
|
||||||
|
**/
|
||||||
|
public abstract class AbstractListModelBuilder
|
||||||
|
extends LockableImpl implements ListModelBuilder {
|
||||||
|
|
||||||
|
public abstract ListModel makeModel(List l, PageState state);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.list;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.List;
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.ControlLink;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default renderer for list items in a {@link
|
||||||
|
* List}. Used by the <code>List</code> component if no renderer is given
|
||||||
|
* explicitly.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$ */
|
||||||
|
public class DefaultListCellRenderer implements ListCellRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a component that has been configured to display
|
||||||
|
* the specified value. If <code>isSelected</code> is true, returns a
|
||||||
|
* bolded <code>Label</code> containing <code>value.toString()</code>. If
|
||||||
|
* <code>isSelected</code> is not true, returns a
|
||||||
|
* <code>ControlLink</code> labelled with
|
||||||
|
* <code>value.toString()</code>. When the user clicks on the link, that
|
||||||
|
* item becomes selected. */
|
||||||
|
public Component getComponent(List list, PageState state, Object value,
|
||||||
|
String key, int index, boolean isSelected)
|
||||||
|
{
|
||||||
|
Label l = new Label(value.toString());
|
||||||
|
if (isSelected) {
|
||||||
|
l.setFontWeight(Label.BOLD);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
return new ControlLink(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.list;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.List;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a component to output one item in a
|
||||||
|
* <code>List</code>. For example, to output the item either as a link
|
||||||
|
* that, when clicked, will make the item selected or, if the item is
|
||||||
|
* selected will display it as a bold label, you would write the following
|
||||||
|
* code:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class MyListCellRenderer implements ListCellRenderer {
|
||||||
|
*
|
||||||
|
* public Component getComponent(List list, PageState state, Object value,
|
||||||
|
* String key, int index, boolean isSelected) {
|
||||||
|
* Label l = new Label(value.toString());
|
||||||
|
* if (isSelected) {
|
||||||
|
* l.setFontWeight(Label.BOLD);
|
||||||
|
* return l;
|
||||||
|
* }
|
||||||
|
* return new ControlLink(l);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* This is actually exactly what the {@link DefaultListCellRenderer} does.
|
||||||
|
*
|
||||||
|
* @see List
|
||||||
|
* @see DefaultListCellRenderer
|
||||||
|
* @see ListModel
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$ */
|
||||||
|
public interface ListCellRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a component that has been configured to display the specified
|
||||||
|
* value. That component's <code>generateXML</code> or <code>print</code>
|
||||||
|
* method is then called to "render" the cell.
|
||||||
|
*
|
||||||
|
* @param list the <code>List</code> in which this item is being displayed.
|
||||||
|
* @param state represents the state of the current request.
|
||||||
|
* @param value the value returned by
|
||||||
|
* <code>list.getModel(state).getElement()</code>
|
||||||
|
* @param key the value returned by
|
||||||
|
* <code>list.getModel(state).getKey()</code>
|
||||||
|
* @param index the number of the item in the list
|
||||||
|
* @param isSelected true is the item is selected
|
||||||
|
* @return the component used to generate the output for the list item
|
||||||
|
* @pre list != null
|
||||||
|
* @pre state |= null
|
||||||
|
* @pre value != null
|
||||||
|
* @pre key != null
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
Component getComponent(List list, PageState state, Object value,
|
||||||
|
String key, int index, boolean isSelected);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.list;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstraction around a list of values used by the {@link
|
||||||
|
* com.arsdigita.bebop.List} component. Data sources that need to be
|
||||||
|
* displayed with a list component need to implement this
|
||||||
|
* interface. The <code>ListModel</code> provides a read-only view of
|
||||||
|
* the underlying data. The items in the list model can be accessed by
|
||||||
|
* calling {@link #next} repeatedly and accessing the item with calls
|
||||||
|
* to {@link #getKey} and {@link #getElement}. Initially, the list
|
||||||
|
* model is positioned <i>before</i> the first element and calls to
|
||||||
|
* {@link #getKey} and {@link #getElement} should produce a runtime
|
||||||
|
* exception until {@link #next} is called for the first time.
|
||||||
|
*
|
||||||
|
* <p> A <code>ListModel</code> is usually generated for each request that
|
||||||
|
* a list component serves. The <code>List</code> component will use its
|
||||||
|
* <code>ListModelBuilder</code> to generate a new <code>ListModel</code>
|
||||||
|
* whenever it serves a request.
|
||||||
|
*
|
||||||
|
* <p> The items a list model iterates over are identified by two separate
|
||||||
|
* objects: a <i>key</i> that has to be a unique string representation of the
|
||||||
|
* actual list item, and an <i>element</i>, which represents the item itself and
|
||||||
|
* is passed directly to the {@link ListCellRenderer}. For example, a list model
|
||||||
|
* that represents a list of objects that are stored in the database, the key
|
||||||
|
* will usually be a suitable representation of the primary key of the object in
|
||||||
|
* the database, while the element can be as simple as a string with the "pretty
|
||||||
|
* name" of the object. While the element is usually only passed to a
|
||||||
|
* <code>ListCellRenderer</code>, the key will often be communicated to other
|
||||||
|
* components that need to know which item in the list is currently selected and
|
||||||
|
* can be retrieved with a call to {@link
|
||||||
|
* com.arsdigita.bebop.List#getSelectedKey(com.arsdigita.bebop.PageState)}. It
|
||||||
|
* is therefore important that all the components that need to work with the key
|
||||||
|
* generated by a list model agree on how it represents the underlying
|
||||||
|
* (database) object.
|
||||||
|
*
|
||||||
|
* <p> <b>Warning:</b> The signature of <code>getKey</code> will be
|
||||||
|
* changed to <code>Obejct getKey()</code> after ACS 4.6 </p>
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.bebop.List
|
||||||
|
* @see ListCellRenderer
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$ */
|
||||||
|
public interface ListModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to the next list item. If there is no next list
|
||||||
|
* item, return <code>false</code>. The item's key and element can be
|
||||||
|
* accessed with calls to {@link #getKey} and {@link
|
||||||
|
* #getElement}. Initially, the list model is positioned <i>before</i>
|
||||||
|
* the first item, so that <code>next()</code> has to be called once
|
||||||
|
* before it is possible to access the item.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the model is positioned on a valid item
|
||||||
|
* after the call returns. */
|
||||||
|
boolean next();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the element for the current list item. The
|
||||||
|
* concrete type of the object returned is specific to each
|
||||||
|
* implementation of <code>ListModel</code> and should be documented
|
||||||
|
* there.
|
||||||
|
*
|
||||||
|
* <p> This method can only be called successfully if the previous cal to
|
||||||
|
* {@link #next} returned <code>true</code>.
|
||||||
|
*
|
||||||
|
* @return the element for the current list item
|
||||||
|
* @see #next */
|
||||||
|
Object getElement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key that
|
||||||
|
* uniquely identifies the current list item. The key should be a
|
||||||
|
* string that uniquely identifies the list item, at least amongst all
|
||||||
|
* items in the list model. If the key needs to be communicated to
|
||||||
|
* other components, for example to display details about the list
|
||||||
|
* item, all components need to agree how the key identifies the
|
||||||
|
* underlying object. For objects stored in a database, this will
|
||||||
|
* usually be a suitable string representation for the primary key of
|
||||||
|
* the object.
|
||||||
|
*
|
||||||
|
* <p> This method can only be called successfully if the previous cal to
|
||||||
|
* {@link #next} returned <code>true</code>.
|
||||||
|
*
|
||||||
|
* @return identifies the object underlying the list item uniquely
|
||||||
|
* @see #next */
|
||||||
|
String getKey();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.list;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Lockable;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a new {@link ListModel ListModels} for each
|
||||||
|
* request. The builder will often run a database query, constructed from
|
||||||
|
* request specific information in the <code>state</code> variable passed
|
||||||
|
* to {@link #makeModel makeModel} and wrap the result set in a lightweight
|
||||||
|
* implementation of <code>ListModel</code>.
|
||||||
|
*
|
||||||
|
* <p> The <code>ListModelBuilder</code> is used by the {@link List}
|
||||||
|
* component whenever it needs to service a request: it calls
|
||||||
|
* <code>makeModel</code> on the builder to generate a request-specific
|
||||||
|
* <code>ListModel</code> which it then uses for outputting the items in
|
||||||
|
* the list.
|
||||||
|
*
|
||||||
|
* <p> <b>Warning:</b> The signature of <code>makeModel</code> will be
|
||||||
|
* changed to <code>ListModel makeModel(List, PageState)</code> after ACS
|
||||||
|
* 4.6 </p>
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$ */
|
||||||
|
public interface ListModelBuilder extends Lockable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a {@link
|
||||||
|
* ListModel} for the request specified by <code>state</code>. This
|
||||||
|
* method is called at least once, and usually only once, for each
|
||||||
|
* request served by the {@link List} that this
|
||||||
|
* <code>ListModelBuilder</code> has been added to.
|
||||||
|
*
|
||||||
|
* @param state contains the request specific data for the request
|
||||||
|
* @return the abstract representation of the list item for this request */
|
||||||
|
ListModel makeModel(List l, PageState state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,612 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.page;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Bebop;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.globalization.Globalization;
|
||||||
|
import com.arsdigita.globalization.GlobalizationHelper;
|
||||||
|
import com.arsdigita.kernel.KernelConfig;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.templating.Templating;
|
||||||
|
import com.arsdigita.templating.XSLParameterGenerator;
|
||||||
|
import com.arsdigita.templating.XSLTemplate;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.web.Debugger;
|
||||||
|
import com.arsdigita.web.TransformationDebugger;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.jsp.PageContext;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for managing and obtaining a Stylesheet based on the current
|
||||||
|
* request's location in the site map. First, we try to find a stylesheet
|
||||||
|
* specific to this site node. If we can't find one, then we walk up the site
|
||||||
|
* map until we find a parent of this site node that has a stylesheet associated
|
||||||
|
* with it.
|
||||||
|
*
|
||||||
|
* If we haven't found one by the time we reach the root, then we'll do the same
|
||||||
|
* tree walk except we'll look for the stylesheet associated with the
|
||||||
|
* <em>package</em> mounted on each site node.
|
||||||
|
*
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @version $Id: PageTransformer.java 2071 2010-01-28 18:24:06Z pboy $
|
||||||
|
*/
|
||||||
|
public class PageTransformer implements PresentationManager {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(PageTransformer.class);
|
||||||
|
// this keeps track of all of the XSLParameters that can be added to
|
||||||
|
// stylesheets
|
||||||
|
private static final HashMap s_XSLParameters = new HashMap();
|
||||||
|
|
||||||
|
// load the default xsl parameter generators
|
||||||
|
static {
|
||||||
|
s_log.debug("Static initalizer starting...");
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("contextPath",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return request
|
||||||
|
.getContextPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("root-context-prefix",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return Web.getConfig()
|
||||||
|
.getDispatcherContextPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("context-prefix",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return Web.getWebContext()
|
||||||
|
.getRequestURL()
|
||||||
|
.getContextPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("internal-theme",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return Web.getWebContext()
|
||||||
|
.getRequestURL()
|
||||||
|
.getContextPath()
|
||||||
|
+ com.arsdigita.web.URL.INTERNAL_THEME_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("dispatcher-prefix",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return com.arsdigita.web.URL
|
||||||
|
.getDispatcherPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("dcp-on-buttons",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (Bebop.getConfig()
|
||||||
|
.doubleClickProtectionOnButtons()) {
|
||||||
|
return "true";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("dcp-on-links",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (Bebop.getConfig()
|
||||||
|
.doubleClickProtectionOnLinks()) {
|
||||||
|
return "true";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("user-agent",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return request.getHeader(
|
||||||
|
"User-Agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("negotiated-language",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return GlobalizationHelper
|
||||||
|
.getNegotiatedLocale()
|
||||||
|
.getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("selected-language",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
Locale selectedLocale
|
||||||
|
= com.arsdigita.globalization.GlobalizationHelper
|
||||||
|
.getSelectedLocale(request);
|
||||||
|
return (selectedLocale != null)
|
||||||
|
? selectedLocale
|
||||||
|
.toString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("request-scheme",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return request.getScheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("server-name",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return request.getServerName();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("server-port",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return Integer.toString(
|
||||||
|
request.getServerPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
registerXSLParameterGenerator("host",
|
||||||
|
new XSLParameterGenerator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateValue(
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (request.getServerPort()
|
||||||
|
== 80) {
|
||||||
|
return String.format(
|
||||||
|
"%s://%s", request
|
||||||
|
.getScheme(), request
|
||||||
|
.getServerName());
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"%s://%s:%d",
|
||||||
|
request.getScheme(),
|
||||||
|
request
|
||||||
|
.getServerName(),
|
||||||
|
request
|
||||||
|
.getServerPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
s_log.debug("Static initalizer finished.");
|
||||||
|
}
|
||||||
|
// XXX These need to move somewhere else.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to indicate that all xsl templates used should be pulled
|
||||||
|
* from the disk and not from the cache. If this is in the request with a
|
||||||
|
* value of Boolean.TRUE then all XSL Stylesheets are pulled from the disk,
|
||||||
|
* not the cache
|
||||||
|
*/
|
||||||
|
public static final String CACHE_XSL_NONE = "cacheXSLNone";
|
||||||
|
/**
|
||||||
|
* This is used to indicate that the "fancy errors" should be used and that
|
||||||
|
* the errors should be placed in the request for later use. To use this,
|
||||||
|
* have the code place an attribute with this name in the request with the
|
||||||
|
* value of Boolean.TRUE
|
||||||
|
*/
|
||||||
|
public static final String FANCY_ERRORS = "fancyErrors";
|
||||||
|
/**
|
||||||
|
* State flag for preventing caching in every case.
|
||||||
|
*/
|
||||||
|
public static final String CACHE_NONE = "none";
|
||||||
|
/**
|
||||||
|
* State flag for per-user caching.
|
||||||
|
*/
|
||||||
|
public static final String CACHE_USER = "user";
|
||||||
|
/**
|
||||||
|
* State flag for enabling caching in every case.
|
||||||
|
*/
|
||||||
|
public static final String CACHE_WORLD = "world";
|
||||||
|
/**
|
||||||
|
* State flag for disabling HTTP header caching.
|
||||||
|
*/
|
||||||
|
public static final String CACHE_DISABLE = "disable";
|
||||||
|
private static String s_defaultCachePolicy = CACHE_DISABLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default cache behavior for the site.
|
||||||
|
*
|
||||||
|
* @param policy a <code>String</code> policy, one of {@link
|
||||||
|
* #CACHE_NONE}, {@link #CACHE_DISABLE}, {@link #CACHE_USER},
|
||||||
|
* {@link #CACHE_WORLD}
|
||||||
|
*/
|
||||||
|
public static void setDefaultCachePolicy(final String policy) {
|
||||||
|
s_defaultCachePolicy = policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content type of the response and then gets the PrintWriter
|
||||||
|
*/
|
||||||
|
private PrintWriter getWriter(final HttpServletResponse resp,
|
||||||
|
final String contentType,
|
||||||
|
final String charset) {
|
||||||
|
Assert.exists(contentType);
|
||||||
|
Assert.exists(charset);
|
||||||
|
|
||||||
|
resp.setContentType(contentType + "; " + "charset=" + charset);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return resp.getWriter();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
s_log.warn("Using getOutputStream instead of getWriter");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new PrintWriter(new OutputStreamWriter(resp.
|
||||||
|
getOutputStream(),
|
||||||
|
charset));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UncheckedWrapperException(ex);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UncheckedWrapperException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses {@link #servePage(Document, HttpServletRequest,
|
||||||
|
* HttpServletResponse, Map)} to implement the
|
||||||
|
* <code>PresentationManager</code> interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void servePage(final Document doc,
|
||||||
|
final HttpServletRequest req,
|
||||||
|
final HttpServletResponse resp) {
|
||||||
|
servePage(doc, req, resp, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serves an XML Document, getting and applying the appropriate XSLT. Also
|
||||||
|
* allows for parameters to be set for the transformer. These will become
|
||||||
|
* top-level xsl:params in the stylesheet. The "contextPath" parameter will
|
||||||
|
* always be passed to XSLT, which is the value of
|
||||||
|
* <code>req.getWebContextPath()</code>.
|
||||||
|
*
|
||||||
|
* @param doc the Bebop page to serve
|
||||||
|
* @param req the servlet request
|
||||||
|
* @param resp the servlet response
|
||||||
|
* @param params a set of name-value pairs to pass as parameters to the
|
||||||
|
* Transformer
|
||||||
|
*/
|
||||||
|
public void servePage(final Document doc,
|
||||||
|
final HttpServletRequest req,
|
||||||
|
final HttpServletResponse resp,
|
||||||
|
final Map params) {
|
||||||
|
if (resp.isCommitted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.exists(doc, Document.class);
|
||||||
|
Assert.exists(req, HttpServletRequest.class);
|
||||||
|
Assert.exists(resp, HttpServletResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String charset = Globalization
|
||||||
|
.getDefaultCharset(DispatcherHelper.getNegotiatedLocale());
|
||||||
|
|
||||||
|
final String output = req.getParameter("output");
|
||||||
|
s_log.info("output=" + output);
|
||||||
|
|
||||||
|
if (output == null) {
|
||||||
|
|
||||||
|
boolean fancyErrors = Bebop.getConfig().wantFancyXSLErrors()
|
||||||
|
|| Boolean.TRUE.equals(req
|
||||||
|
.getAttribute(
|
||||||
|
FANCY_ERRORS));
|
||||||
|
|
||||||
|
// Get the stylesheet transformer object corresponding to the
|
||||||
|
// current request.
|
||||||
|
final XSLTemplate template = Templating.getTemplate(
|
||||||
|
req,
|
||||||
|
fancyErrors,
|
||||||
|
!Boolean.TRUE.equals(req.getAttribute(CACHE_XSL_NONE)));
|
||||||
|
|
||||||
|
final PrintWriter writer = getWriter(resp, "text/html", charset);
|
||||||
|
|
||||||
|
final Transformer xf = template.newTransformer();
|
||||||
|
endTransaction(req);
|
||||||
|
|
||||||
|
// Transformers are not thread-safe, so we assume we have
|
||||||
|
// exclusive use of xf here. But we could recycle it.
|
||||||
|
xf.clearParameters();
|
||||||
|
|
||||||
|
if (params != null) {
|
||||||
|
final Iterator entries = params.entrySet().iterator();
|
||||||
|
|
||||||
|
while (entries.hasNext()) {
|
||||||
|
final Map.Entry entry = (Map.Entry) entries.next();
|
||||||
|
|
||||||
|
xf.setParameter((String) entry.getKey(),
|
||||||
|
entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addXSLParameters(xf, req);
|
||||||
|
|
||||||
|
// This has no effect on the resulting encoding of the
|
||||||
|
// output generated by the XSLT transformer. Why?
|
||||||
|
// Because we pass the transformer an instance of the
|
||||||
|
// Writer class. The Writer class provides no methods
|
||||||
|
// for changing the encoding. So, the only thing this
|
||||||
|
// does is, it causes the transformer to include a
|
||||||
|
// line like <meta http-equiv="Content-Type"
|
||||||
|
// content="text/html; charset=foo"> in the output
|
||||||
|
// document.
|
||||||
|
xf.setOutputProperty("encoding", charset);
|
||||||
|
|
||||||
|
try {
|
||||||
|
xf.transform(new DOMSource(doc.getInternalDocument()),
|
||||||
|
new StreamResult(writer));
|
||||||
|
} catch (TransformerException ex) {
|
||||||
|
throw new UncheckedWrapperException(
|
||||||
|
"cannot transform document", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy and paste from BasePresentationManager
|
||||||
|
if (KernelConfig.getConfig().isDebugEnabled()) {
|
||||||
|
Document origDoc = (Document) req.getAttribute(
|
||||||
|
"com.arsdigita.xml.Document");
|
||||||
|
Debugger.addDebugger(new TransformationDebugger(template.
|
||||||
|
getSource(), template.getDependents()));
|
||||||
|
writer.print(Debugger.getDebugging(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (output.equals("xml")) {
|
||||||
|
endTransaction(req);
|
||||||
|
|
||||||
|
final PrintWriter writer = getWriter(resp, "text/xml", charset);
|
||||||
|
|
||||||
|
DispatcherHelper.forceCacheDisable(resp);
|
||||||
|
|
||||||
|
writer.println(doc.toString(true));
|
||||||
|
} else if (output.equals("xsl")) {
|
||||||
|
XSLTemplate template = null;
|
||||||
|
try {
|
||||||
|
// Get the stylesheet transformer object corresponding to
|
||||||
|
// the
|
||||||
|
// current request.
|
||||||
|
template = Templating.getTemplate(req,
|
||||||
|
Boolean.TRUE.equals(req
|
||||||
|
.getAttribute(
|
||||||
|
PageTransformer.FANCY_ERRORS)),
|
||||||
|
!Boolean.TRUE.equals(req
|
||||||
|
.getAttribute(
|
||||||
|
PageTransformer.CACHE_XSL_NONE)));
|
||||||
|
endTransaction(req);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Date now = new Date();
|
||||||
|
SimpleDateFormat fmt = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd-HH-mm");
|
||||||
|
String prefix = "waf-xsl-" + fmt.format(now);
|
||||||
|
|
||||||
|
final OutputStream os = resp.getOutputStream();
|
||||||
|
resp.reset();
|
||||||
|
resp.setContentType("application/zip");
|
||||||
|
resp.setHeader("Content-Disposition",
|
||||||
|
"attachment; filename=\"" + prefix + ".zip\"");
|
||||||
|
DispatcherHelper.forceCacheDisable(resp);
|
||||||
|
|
||||||
|
template.toZIP(os, prefix);
|
||||||
|
|
||||||
|
resp.flushBuffer();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UncheckedWrapperException(ex);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(output
|
||||||
|
+ " is an unknown output");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the current transaction. Is a performance optimization to end ASAP
|
||||||
|
* before serving the page.
|
||||||
|
*
|
||||||
|
* @param req HTTP request.
|
||||||
|
*/
|
||||||
|
private void endTransaction(final HttpServletRequest req) {
|
||||||
|
// There is no longer any need for a database handle.
|
||||||
|
if (req.getAttribute(PageContext.EXCEPTION) == null) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This adds a generator to the list of parameters available to
|
||||||
|
* XSLStylesheets. If this is called a second time with the same parameter
|
||||||
|
* name then all previous calls are overwritten and only the last registered
|
||||||
|
* generator is used.
|
||||||
|
*
|
||||||
|
* @param parameterName
|
||||||
|
* @param parameterGenerator
|
||||||
|
*/
|
||||||
|
public static void registerXSLParameterGenerator(String parameterName,
|
||||||
|
XSLParameterGenerator parameterGenerator) {
|
||||||
|
s_XSLParameters.put(parameterName, parameterGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes the parameter from the list of parameters that will be added
|
||||||
|
* to stylesheets
|
||||||
|
*
|
||||||
|
* @param parameterName
|
||||||
|
*/
|
||||||
|
public static void removeXSLParameterGenerator(String parameterName) {
|
||||||
|
s_XSLParameters.remove(parameterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Collection of all names of XSL Parameters that have been
|
||||||
|
* registered
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Collection getXSLParameterNames() {
|
||||||
|
return s_XSLParameters.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This takes a name and request and returns the value that should be used
|
||||||
|
* in the XSL for the given name
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param request
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getXSLParameterValue(String name,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
XSLParameterGenerator generator
|
||||||
|
= (XSLParameterGenerator) s_XSLParameters
|
||||||
|
.get(name);
|
||||||
|
if (generator != null) {
|
||||||
|
return generator.generateValue(request);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This takes in a transformer and adds all of the registered xsl
|
||||||
|
* paraemters.
|
||||||
|
*
|
||||||
|
* @param transformer
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public static void addXSLParameters(Transformer transformer,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
final Iterator entries = s_XSLParameters.entrySet().iterator();
|
||||||
|
|
||||||
|
while (entries.hasNext()) {
|
||||||
|
final Map.Entry entry = (Map.Entry) entries.next();
|
||||||
|
|
||||||
|
String value = ((XSLParameterGenerator) entry.getValue()).
|
||||||
|
generateValue(request);
|
||||||
|
if (value == null) {
|
||||||
|
// XSL does not like nulls
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
transformer.setParameter((String) entry.getKey(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.globalization.Globalization;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an array of values
|
||||||
|
* and converts them to a Java object array. The values in the array can be
|
||||||
|
* any parameter type, for example strings, big decimals etc. The values
|
||||||
|
* are read from a request either as multiple repeated values with the same
|
||||||
|
* parameter name, i.e. from a URL like
|
||||||
|
* <tt>http://foo.com/page?array=one&array=two</tt> or from an encoded
|
||||||
|
* string that was previously produced by {@link #unmarshal unmarshal}.
|
||||||
|
* <p>
|
||||||
|
* Either way, the transformed value of the parameters is an array of the
|
||||||
|
* type of <code>getElementParameter().getValueClass()[]</code>, so if the
|
||||||
|
* element parameter is a <code>StringParameter</code>, the transformed
|
||||||
|
* value will be of type <code>String[]</code>, and if it is a
|
||||||
|
* <code>BigDecimalParameter</code>, it will be of type
|
||||||
|
* <code>BigDecimal[]</code>.
|
||||||
|
*
|
||||||
|
* <p> <b>Warning:</b> The array parameter does currently no checking
|
||||||
|
* related to the size of the array, no matter what you set min and max
|
||||||
|
* value counts to.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ArrayParameter extends ParameterModel {
|
||||||
|
|
||||||
|
// Used to separate entries in the marshalled form.
|
||||||
|
private static final char SEP_CHAR = '^';
|
||||||
|
// Used to escape SEP_CHAR and ESCAPE_CHAR in the marshalled form.
|
||||||
|
private static final char ESCAPE_CHAR = '.';
|
||||||
|
|
||||||
|
|
||||||
|
protected int maxCount=Integer.MAX_VALUE;
|
||||||
|
protected int minCount=0;
|
||||||
|
|
||||||
|
// The model for the entries in the array
|
||||||
|
private ParameterModel m_element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array parameter. Entries in the array are of type
|
||||||
|
* <code>String</code>.
|
||||||
|
*
|
||||||
|
* @param name the name of the array parameter in URIs.
|
||||||
|
*/
|
||||||
|
public ArrayParameter(String name) {
|
||||||
|
this(new StringParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new array parameter. Entries in the array are of the type
|
||||||
|
* produced by <code>element</code>. Validation listeners on the
|
||||||
|
* <code>element</code> are run for each array entry.
|
||||||
|
*
|
||||||
|
* @param element the parameter model for entries in the array.
|
||||||
|
*/
|
||||||
|
public ArrayParameter(ParameterModel element) {
|
||||||
|
super(element.getName());
|
||||||
|
m_element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the parameter model that transforms and validates individual
|
||||||
|
* entries in the array.
|
||||||
|
*
|
||||||
|
* @return the parameter model for individual entries in the array.
|
||||||
|
*/
|
||||||
|
public final ParameterModel getElementParameter() {
|
||||||
|
return m_element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the parameter model that transforms and validates individual
|
||||||
|
* entries in the array.
|
||||||
|
*
|
||||||
|
* @param v the parameter model for entries in the array.
|
||||||
|
*/
|
||||||
|
public final void setElementParameter(ParameterModel v) {
|
||||||
|
Assert.isUnlocked(this);
|
||||||
|
m_element = v;
|
||||||
|
setName(v.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the given value into a string. The returned string can be
|
||||||
|
* turned back into an object that equals <code>value</code> by {@link
|
||||||
|
* #unmarshalElement}. Uses the marshalling of the element parameter
|
||||||
|
* model.
|
||||||
|
*
|
||||||
|
* @param value an object with the type of array elements
|
||||||
|
* @return the value encoded in a string
|
||||||
|
* @see ParameterModel#marshal
|
||||||
|
* @see #unmarshalElement
|
||||||
|
*/
|
||||||
|
public final String marshalElement(Object value) {
|
||||||
|
return getElementParameter().marshal(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode one array element from its string representation. The string
|
||||||
|
* <code>encoded</code> must have been produced through the marshalling
|
||||||
|
* of the element parameter.
|
||||||
|
*
|
||||||
|
* @param encoded the marshalled representation of an array element
|
||||||
|
* @return the array element represented by <code>encoded</code>
|
||||||
|
* @see ParameterModel#unmarshal
|
||||||
|
* @see #marshalElement
|
||||||
|
*/
|
||||||
|
public final Object unmarshalElement(String encoded) {
|
||||||
|
return getElementParameter().unmarshal(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* private void checkValueCount(Object[] parameterValues,
|
||||||
|
* ParameterModel parameterModel) {
|
||||||
|
*
|
||||||
|
* if (parameterValues == null) return;
|
||||||
|
*
|
||||||
|
* int parameterValueCount = (parameterValues == null) ? 0 :
|
||||||
|
* parameterValues.length;
|
||||||
|
*
|
||||||
|
* if (! parameterModel.checkValueCount(parameterValueCount)) {
|
||||||
|
*
|
||||||
|
* String msgFormat = resources.getString("INVALID_NUMBER_OF_VALUES");
|
||||||
|
* Object[] msgArgs = { new Integer(parameterModel.getMinValueCount()),
|
||||||
|
* new Integer(parameterModel.getMaxValueCount()) };
|
||||||
|
*
|
||||||
|
* String msg = MessageFormat.format(msgFormat, msgArgs);
|
||||||
|
*
|
||||||
|
* addError(parameterModel.getName(), msg);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract an array of values from the request. Autodetects whether the
|
||||||
|
* request parameter contains the array in several parameters with
|
||||||
|
* identical name or as just one value in an encoded form produced by
|
||||||
|
* {@link #marshal}.
|
||||||
|
*
|
||||||
|
* @param request a <code>HttpServletRequest</code> value
|
||||||
|
* @return an <code>Object</code> value
|
||||||
|
*/
|
||||||
|
public Object transformValue(HttpServletRequest request) {
|
||||||
|
String[] values = Globalization.decodeParameters(request, getName());
|
||||||
|
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ( values.length != 1 ) {
|
||||||
|
Object[] result = makeElementArray(values.length);
|
||||||
|
for ( int i=0; i < result.length; i++ ) {
|
||||||
|
result[i] = unmarshalElement(values[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshal(values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the value which must be an array of objects into one
|
||||||
|
* string. The entries in the array must be of a type that can be
|
||||||
|
* understood by the {@link #getElementParameter element
|
||||||
|
* parameter}. The {@link #unmarshal} method can read this format and
|
||||||
|
* reconstruct the array from that string again.
|
||||||
|
*
|
||||||
|
* @param value an array of values
|
||||||
|
* @return the array encoded into one string
|
||||||
|
* @pre value instanceof Object[]
|
||||||
|
*/
|
||||||
|
public String marshal(Object value) {
|
||||||
|
if ( value == null ) return null;
|
||||||
|
|
||||||
|
Object[] values = (Object[]) value;
|
||||||
|
|
||||||
|
if ( values.length == 0 ) {
|
||||||
|
return null;
|
||||||
|
} else if ( values.length == 1 ) {
|
||||||
|
return marshalElement(values[0]);
|
||||||
|
}
|
||||||
|
StringBuffer result = new StringBuffer(400);
|
||||||
|
for (int i=0; i < values.length; i++) {
|
||||||
|
result.append(SEP_CHAR);
|
||||||
|
encode(result, marshalElement(values[i]));
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode one entry in the array by escaping the 'right'
|
||||||
|
* characters. The <code>value</code> should be the result of
|
||||||
|
* marshalling one array entry through the element parameter.
|
||||||
|
*
|
||||||
|
* @param buf the buffer to whic hthe escaped string is appended
|
||||||
|
* @param value marshalled representation of one array element
|
||||||
|
*/
|
||||||
|
private void encode(StringBuffer buf, String value) {
|
||||||
|
for (int i=0; i < value.length(); i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if ( c == SEP_CHAR || c == ESCAPE_CHAR ) {
|
||||||
|
buf.append(ESCAPE_CHAR);
|
||||||
|
}
|
||||||
|
buf.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the string representation of an array into an array of
|
||||||
|
* objects. The returned value is of type <code>Object[]</code>. The
|
||||||
|
* type of the entries in the array depends on the {@link
|
||||||
|
* #getElementParameter element parameter}.
|
||||||
|
*
|
||||||
|
* @param value the marshalled version of an array.
|
||||||
|
* @return the <code>Object[]</code> reconstructed from
|
||||||
|
* <code>value</code>.
|
||||||
|
*/
|
||||||
|
public Object unmarshal(String value) {
|
||||||
|
if ( value.length() == 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ( ! value.startsWith("" + SEP_CHAR) ) {
|
||||||
|
Object[] result = makeElementArray(1);
|
||||||
|
result[0] = unmarshalElement(value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// This is the hard part: we have an array encoded in one string
|
||||||
|
// before us.
|
||||||
|
boolean escape = false;
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
ArrayList l = new ArrayList();
|
||||||
|
for (int end = 1; end < value.length(); end++) {
|
||||||
|
char c = value.charAt(end);
|
||||||
|
if ( escape ) {
|
||||||
|
buf.append(c);
|
||||||
|
escape = false;
|
||||||
|
} else {
|
||||||
|
if ( c == SEP_CHAR ) {
|
||||||
|
l.add(unmarshalElement(buf.toString()));
|
||||||
|
buf = new StringBuffer();
|
||||||
|
} else if ( c == ESCAPE_CHAR ) {
|
||||||
|
escape = true;
|
||||||
|
} else {
|
||||||
|
buf.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( buf.length() > 0 ) {
|
||||||
|
if ( escape ) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Garbled string encoding of array: '" + value + "'");
|
||||||
|
} else {
|
||||||
|
l.add(unmarshalElement(buf.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There was only one value. Since we don't encode arrays of length
|
||||||
|
// one, this means that the one entry really did start with the
|
||||||
|
// SEP_CHAR
|
||||||
|
if ( l.size() == 1 ) {
|
||||||
|
Object[] result = makeElementArray(1);
|
||||||
|
result[0] = unmarshalElement((String) l.get(0));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return l.toArray(makeElementArray(l.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum number of values for this parameter that may be
|
||||||
|
* submitted for the request to be valid. If the parameter is required,
|
||||||
|
* the minimum must be at least one.
|
||||||
|
*/
|
||||||
|
public final void setMinValueCount(int count) {
|
||||||
|
minCount=count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of values for this parameter that may be
|
||||||
|
* submitted for the request to be valid.
|
||||||
|
*/
|
||||||
|
public final void setMaxValueCount(int count) {
|
||||||
|
minCount=count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum number of values for this parameter that may be
|
||||||
|
* submitted for the request to be valid.
|
||||||
|
*/
|
||||||
|
public final int getMinValueCount() {
|
||||||
|
return minCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum number of values for this parameter that may be
|
||||||
|
* submitted for the request to be valid.
|
||||||
|
*/
|
||||||
|
public final int getMaxValueCount() {
|
||||||
|
return maxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the array extracted from the request by running the
|
||||||
|
* validation listeners of this parameter, and by running thte
|
||||||
|
* validation for the element parameter for each entry in the array
|
||||||
|
* separately.
|
||||||
|
*
|
||||||
|
* @param data the parameter data to validate.
|
||||||
|
* @throws FormProcessException if the data can not be validated.
|
||||||
|
*/
|
||||||
|
public void validate(ParameterData data) throws FormProcessException {
|
||||||
|
super.validate(data);
|
||||||
|
Object[] values = (Object[]) data.getValue();
|
||||||
|
if (values != null) {
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
ParameterData p = new ParameterData(
|
||||||
|
getElementParameter(), values[i]
|
||||||
|
);
|
||||||
|
getElementParameter().validate(p);
|
||||||
|
data.copyErrors(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void lock() {
|
||||||
|
getElementParameter().lock();
|
||||||
|
super.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return makeElementArray(0).getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] makeElementArray(int length) {
|
||||||
|
return (Object[]) Array.newInstance(getElementParameter().getValueClass(),
|
||||||
|
length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class which represents a BigDecimal
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class BigDecimalParameter extends ParameterModel {
|
||||||
|
|
||||||
|
public BigDecimalParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unmarshal(String encoded)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
if (encoded == null || encoded.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new BigDecimal(encoded);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() + " should be a BigDecimal: '" + encoded + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getValueClass() {
|
||||||
|
return BigDecimal.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class which represents a BigInteger
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class BigIntegerParameter extends ParameterModel {
|
||||||
|
|
||||||
|
public BigIntegerParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
if (encoded == null || encoded.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new BigInteger(encoded);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() + " should be a BigInteger: '" + encoded + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return BigInteger.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode and decode a bit set as a request parameter. The bit set is
|
||||||
|
* stored in a {@link java.util.BitSet} and encoded and decoded from
|
||||||
|
* the HTTP request appropriately.
|
||||||
|
*
|
||||||
|
* Currently two forms of encoding are supported: RAW encoding and
|
||||||
|
* DGap. RAW encoding stuffs the bits straight into characters. <a
|
||||||
|
* href="http://bmagic.sourceforge.net/dGap.html">DGap</a> is a
|
||||||
|
* variation on run length encoding particularly suited to
|
||||||
|
* bitsets. The most appropriate encoding to use depends on the
|
||||||
|
* distribution of bits. Since it iss not always possible to
|
||||||
|
* anticipate this distribution, an automatic encoding mode is
|
||||||
|
* provided which chooses the encoding with the shortest resulting
|
||||||
|
* string size.
|
||||||
|
*
|
||||||
|
* @author David Lutterkort
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class BitSetParameter extends ParameterModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The radix used to encode/decode the bitset into a BigInteger
|
||||||
|
*/
|
||||||
|
private static final int RADIX = Character.MAX_RADIX; //currently 26
|
||||||
|
|
||||||
|
/** Compression coefficient for the raw encoding. Base-26 encoding is this
|
||||||
|
* many times more compact than base-2 encoding.
|
||||||
|
*/
|
||||||
|
private static final double QUOTIENT = Math.log(2) / Math.log(RADIX);
|
||||||
|
|
||||||
|
private static final char SEPARATOR = '.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for RAW encoding of bit set
|
||||||
|
*/
|
||||||
|
public static final int ENCODE_RAW = 0;
|
||||||
|
/**
|
||||||
|
* Flag for DGap encoding of bit set
|
||||||
|
*/
|
||||||
|
public static final int ENCODE_DGAP = 1;
|
||||||
|
/**
|
||||||
|
* Flag to automatically choose the shortest encoding scheme
|
||||||
|
*/
|
||||||
|
public static final int ENCODE_AUTO = 2;
|
||||||
|
|
||||||
|
private int m_encode = ENCODE_RAW;
|
||||||
|
|
||||||
|
private static final String FLAG_RAW = "r";
|
||||||
|
private static final String FLAG_DGAP = "d";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a bit set parameter with the given name.
|
||||||
|
*
|
||||||
|
* @param name the name of this parameter for use in URLs etc.
|
||||||
|
* @param encode the encoding scheme
|
||||||
|
*/
|
||||||
|
public BitSetParameter(String name, int encode) {
|
||||||
|
super(name);
|
||||||
|
m_encode = encode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a bit set parameter with the given name, defaulting
|
||||||
|
* to the RAW encoding scheme
|
||||||
|
*
|
||||||
|
* @param name the name of this parameter for use in URLs etc.
|
||||||
|
*/
|
||||||
|
public BitSetParameter(String name) {
|
||||||
|
this(name, ENCODE_RAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a bit set from the HTTP request. Looks for a parameter with
|
||||||
|
* the name of this parameter and unmarshals it into a {@link
|
||||||
|
* java.util.BitSet}.
|
||||||
|
*
|
||||||
|
* @param request the HTTP request
|
||||||
|
* @return the {@link java.util.BitSet} extracted from the request or
|
||||||
|
* <code>null</code>.
|
||||||
|
* @throws IllegalArgumentException if the parameter can not be
|
||||||
|
* transformed into a bit set.
|
||||||
|
*/
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a string in the format produced by {@link #marshal marshal} and
|
||||||
|
* produce the corresponding {@link java.util.BitSet}.
|
||||||
|
*
|
||||||
|
* @param value A string representing the bit set.
|
||||||
|
* @return the {@link java.util.BitSet} corresponding to the value.
|
||||||
|
* @throws IllegalArgumentException if the value can not be
|
||||||
|
* transformed into a bit set.
|
||||||
|
* @see #marshal
|
||||||
|
*/
|
||||||
|
public Object unmarshal(String value)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
if (value.startsWith(FLAG_DGAP)) {
|
||||||
|
return unmarshalDGap(value.substring(1));
|
||||||
|
} else if (value.startsWith(FLAG_RAW)) {
|
||||||
|
return unmarshalRaw(value.substring(1));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() + " should start with either '" +
|
||||||
|
FLAG_RAW + "' or '" + FLAG_DGAP + "' : "+ value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object unmarshalRaw(String value)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
BitSet result = new BitSet(32);
|
||||||
|
BigInteger n = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
n = new BigInteger(value, RADIX);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() + " should be a BigInteger: '" + value + "'; " +
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < n.bitLength(); i++) {
|
||||||
|
if ( n.testBit(i) ) {
|
||||||
|
result.set(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object unmarshalDGap(String value)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
BitSet result = new BitSet(32);
|
||||||
|
String[] bits = StringUtils.split(value, SEPARATOR);
|
||||||
|
|
||||||
|
boolean state = ("0".equals(bits[0]) ? false : true);
|
||||||
|
int current = 0;
|
||||||
|
// byline patch
|
||||||
|
// if (state) {
|
||||||
|
// result.set(1);
|
||||||
|
// }
|
||||||
|
for (int i = 1 ; i < bits.length ; i++) {
|
||||||
|
BigInteger n = null;
|
||||||
|
try {
|
||||||
|
n = new BigInteger(bits[i], RADIX);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() + " should be a comma separated list of BigInteger: '" +
|
||||||
|
value + "'; " + e.getMessage());
|
||||||
|
}
|
||||||
|
int length = n.intValue();
|
||||||
|
|
||||||
|
for (int j = 0 ; j < length ; j++) {
|
||||||
|
if (state) {
|
||||||
|
result.set(current);
|
||||||
|
}
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = !state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn the value, which must be a {@link java.util.BitSet}, into a
|
||||||
|
* string that can be read by {@link #unmarshal unmarshal}.
|
||||||
|
*
|
||||||
|
* @param value a {@link java.util.BitSet} produced by this parameter
|
||||||
|
* model.
|
||||||
|
* @return a string encoding of the bit set.
|
||||||
|
* @see #unmarshal
|
||||||
|
*/
|
||||||
|
public String marshal(Object value) {
|
||||||
|
if ( value == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitSet set = (BitSet) value;
|
||||||
|
|
||||||
|
if (m_encode == ENCODE_DGAP) {
|
||||||
|
return FLAG_DGAP + marshalDGap(set);
|
||||||
|
} else if (m_encode == ENCODE_AUTO) {
|
||||||
|
String dgap = marshalDGap(set);
|
||||||
|
if ( rawLength(set) > dgap.length() ) {
|
||||||
|
return FLAG_DGAP + dgap;
|
||||||
|
} else {
|
||||||
|
return FLAG_RAW + marshalRaw(set);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return FLAG_RAW + marshalRaw(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the length of the base-26 representation of the bitset.
|
||||||
|
**/
|
||||||
|
private static int rawLength(BitSet set) {
|
||||||
|
if ( set.length()==0 ) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return (int) Math.ceil(set.length() * QUOTIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String marshalRaw(BitSet set) {
|
||||||
|
BigInteger n = new BigInteger("0");
|
||||||
|
for (int i=0; i<set.length(); i++) {
|
||||||
|
if (set.get(i)) {
|
||||||
|
// TODO: this may be horribly inefficient. It allocates a new
|
||||||
|
// BigInteger for every set bit
|
||||||
|
// It is better to convert the bit set to a byte[] by hand
|
||||||
|
// and then pass that to the BigInteger constructor
|
||||||
|
n = n.setBit(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n.toString(RADIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String marshalDGap(BitSet set) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append(set.get(0) ? "1" : "0");
|
||||||
|
|
||||||
|
boolean current = set.get(0);
|
||||||
|
int runLength = 1;
|
||||||
|
for (int i=1; i < set.length(); i++) {
|
||||||
|
if ( set.get(i) == current ) {
|
||||||
|
runLength++;
|
||||||
|
} else {
|
||||||
|
sb.append(SEPARATOR);
|
||||||
|
BigInteger bi = new BigInteger(String.valueOf(runLength));
|
||||||
|
sb.append(bi.toString(RADIX));
|
||||||
|
runLength = 1;
|
||||||
|
current = set.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(SEPARATOR);
|
||||||
|
sb.append(String.valueOf(runLength));
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return BitSet.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents the model for boolean form parameters.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BooleanParameter extends ParameterModel {
|
||||||
|
|
||||||
|
public BooleanParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object transformValue(HttpServletRequest request) {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded) {
|
||||||
|
return new Boolean(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return Boolean.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.RequestLocal;
|
||||||
|
import com.arsdigita.bebop.event.ParameterEvent;
|
||||||
|
import com.arsdigita.bebop.event.ParameterListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic validation listener that wraps any parameter listener so
|
||||||
|
* that the parameter listener is conditionally run based on the value
|
||||||
|
* of a RequestLocal. The constructor takes in a parameter listener
|
||||||
|
* and a RequestLocal that returns a Boolean. The request local is
|
||||||
|
* lazily evaluated when the validation listener is run. A typical
|
||||||
|
* code block for the request local:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* private RequestLocal m_isCancel = new RequestLocal() {
|
||||||
|
* public Object initialValue(PageState ps) {
|
||||||
|
* if ( m_submit.isSelected(ps) ) {
|
||||||
|
* return Boolean.FALSE;
|
||||||
|
* } else {
|
||||||
|
* return Boolean.TRUE;
|
||||||
|
* }
|
||||||
|
* }};
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Which only returns false if the main submit button is selected.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CancellableValidationListener implements ParameterListener {
|
||||||
|
|
||||||
|
private RequestLocal m_isCancel;
|
||||||
|
private GlobalizedParameterListener m_listener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @parameter l The listener that should be fired if this is not a
|
||||||
|
* cancel event.
|
||||||
|
* @parameter isCancel a Boolean RequestLocal that is true if this
|
||||||
|
* is a cancel event; otherwise false.
|
||||||
|
* */
|
||||||
|
public CancellableValidationListener(GlobalizedParameterListener l,
|
||||||
|
RequestLocal isCancel) {
|
||||||
|
m_isCancel = isCancel;
|
||||||
|
m_listener = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate(ParameterEvent evt) throws FormProcessException {
|
||||||
|
PageState ps = evt.getPageState();
|
||||||
|
Boolean b = (Boolean) m_isCancel.get(ps);
|
||||||
|
|
||||||
|
if ( b == Boolean.TRUE ) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
m_listener.validate(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.parameters.ParameterData;
|
||||||
|
import com.arsdigita.bebop.event.ParameterListener;
|
||||||
|
import com.arsdigita.bebop.event.ParameterEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the
|
||||||
|
* parameter's value is within a specified date range.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Stas Freidin
|
||||||
|
* @author Rory Solomon
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DateInRangeValidationListener implements ParameterListener {
|
||||||
|
|
||||||
|
private final Date m_lowerBound;
|
||||||
|
private final Date m_upperBound;
|
||||||
|
private final String m_baseErrorMsg;
|
||||||
|
|
||||||
|
public DateInRangeValidationListener(Date lower, Date upper) {
|
||||||
|
if ( lower.compareTo(upper) > 0 ) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Lower bound must be earlier than or equal to upper bound.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lowerBound = lower;
|
||||||
|
m_upperBound = upper;
|
||||||
|
|
||||||
|
DateFormat formatter = DateFormat.getDateInstance();
|
||||||
|
StringBuffer msg = new StringBuffer(128);
|
||||||
|
msg.append("The following dates are out of the specified range of (")
|
||||||
|
.append(formatter.format(m_lowerBound))
|
||||||
|
.append(",")
|
||||||
|
.append(formatter.format(m_upperBound))
|
||||||
|
.append(") :");
|
||||||
|
|
||||||
|
m_baseErrorMsg = msg.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate (ParameterEvent e) {
|
||||||
|
// gets the date format for the default style and locale
|
||||||
|
DateFormat formatter = DateFormat.getDateInstance();
|
||||||
|
|
||||||
|
ParameterData data = e.getParameterData();
|
||||||
|
Object obj = data.getValue();
|
||||||
|
|
||||||
|
// Another listener will validate that these values are present if
|
||||||
|
// required, but we don't want any null pointer exceptions.
|
||||||
|
if ( obj == null ) return;
|
||||||
|
|
||||||
|
boolean isValid = true;
|
||||||
|
|
||||||
|
StringBuffer msg = null;
|
||||||
|
if ( data.getValue() instanceof Object[] ) {
|
||||||
|
Date[] values = (Date[]) obj;
|
||||||
|
|
||||||
|
for (int i = 0; i < values.length; i += 1) {
|
||||||
|
final Date value = values[i];
|
||||||
|
if (isOutOfRange(value)) {
|
||||||
|
if (msg == null) {
|
||||||
|
msg = makeErrorBuffer();
|
||||||
|
}
|
||||||
|
msg.append(" ").append(formatter.format(value));
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final Date value = (Date) obj;
|
||||||
|
if (isOutOfRange(value)) {
|
||||||
|
msg = makeErrorBuffer();
|
||||||
|
msg.append(" ").append(formatter.format(value));
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isValid) {
|
||||||
|
data.addError(msg.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOutOfRange(final Date value) {
|
||||||
|
return m_lowerBound.compareTo(value) >= 0
|
||||||
|
|| value.compareTo(m_upperBound) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuffer makeErrorBuffer() {
|
||||||
|
return new StringBuffer(m_baseErrorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import com.arsdigita.globalization.Globalization;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents the model for date form parameters.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DateParameter extends ParameterModel {
|
||||||
|
|
||||||
|
public DateParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns a new Calendar object that is manipulated within transformValue to create
|
||||||
|
* a Date Object. This method should be overridden if you wish to use a Calendar other than the
|
||||||
|
* lenient GregorianCalendar.
|
||||||
|
*
|
||||||
|
* @param request the servlet request from which Locale can be extracted if needed
|
||||||
|
*
|
||||||
|
* @return a new Calendar object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected Calendar getCalendar(HttpServletRequest request) {
|
||||||
|
return new GregorianCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a date object from multiple parameters in the request. This method searches for
|
||||||
|
* parameters named <code>getName() + ".year"<code>, <code>getName() +
|
||||||
|
* ".month"<code> and <code>getName() + ".day"<code>. It sets the
|
||||||
|
* fields <code>HOUR</code>, <code>MINUTE</code> and <code>SECOND</code> to 0, since they are by
|
||||||
|
* default the current time.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
Calendar c = null;
|
||||||
|
Object outVal = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
c = getCalendar(request);
|
||||||
|
c.clear();
|
||||||
|
//don't accept lenient dates like June 44
|
||||||
|
c.setLenient(false);
|
||||||
|
|
||||||
|
String year = Globalization.decodeParameter(request, getName() + ".year");
|
||||||
|
String month = Globalization.decodeParameter(request, getName() + ".month");
|
||||||
|
String day = Globalization.decodeParameter(request, getName() + ".day");
|
||||||
|
|
||||||
|
if (year == null && month == null && day == null) {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
if (day == null || day.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (year != null) {
|
||||||
|
c.set(Calendar.YEAR, Integer.parseInt(year));
|
||||||
|
}
|
||||||
|
if (month != null) {
|
||||||
|
c.set(Calendar.MONTH, Integer.parseInt(month));
|
||||||
|
}
|
||||||
|
if (day != null) {
|
||||||
|
c.set(Calendar.DATE, Integer.parseInt(day));
|
||||||
|
}
|
||||||
|
outVal = c.getTime();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid Day of Month");
|
||||||
|
}
|
||||||
|
return outVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded) {
|
||||||
|
try {
|
||||||
|
return new Date(Long.parseLong(encoded));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot unmarshal date '"
|
||||||
|
+ encoded + "': " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String marshal(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return Long.toString(((Date) value).getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return Date.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import com.arsdigita.globalization.Globalization;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents the model for date and time form parameters.
|
||||||
|
* (based on the code in DateParameter.java)
|
||||||
|
*
|
||||||
|
* @author Scot Seago
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DateTimeParameter extends ParameterModel {
|
||||||
|
|
||||||
|
public DateTimeParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns a new Calendar object that is manipulated
|
||||||
|
* within transformValue to create a Date Object. This method should
|
||||||
|
* be overridden if you wish to use a Calendar other than the
|
||||||
|
* lenient GregorianCalendar.
|
||||||
|
*
|
||||||
|
* @param request the servlet request from which Locale can be
|
||||||
|
* extracted if needed
|
||||||
|
*
|
||||||
|
* @return a new Calendar object
|
||||||
|
* */
|
||||||
|
protected Calendar getCalendar(HttpServletRequest request) {
|
||||||
|
return new GregorianCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a dateTime object from multiple parameters in the
|
||||||
|
* request. This method searches for parameters named
|
||||||
|
* <code>getName() + ".year"<code>,
|
||||||
|
* <code>getName() + ".month"<code>,
|
||||||
|
* <code>getName() + ".day"<code>,
|
||||||
|
* <code>getName() + ".hour"<code>,
|
||||||
|
* <code>getName() + ".minute"<code>,
|
||||||
|
* <code>getName() + ".second"<code>, and
|
||||||
|
* <code>getName() + ".amOrPm"<code>.
|
||||||
|
* */
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
Calendar c = getCalendar(request);
|
||||||
|
c.clear();
|
||||||
|
|
||||||
|
String year = Globalization.decodeParameter(request, getName()+".year");
|
||||||
|
String month = Globalization.decodeParameter(request, getName()+".month");
|
||||||
|
String day = Globalization.decodeParameter(request, getName()+".day");
|
||||||
|
String hour = Globalization.decodeParameter(request, getName()+".hour");
|
||||||
|
String minute = Globalization.decodeParameter(request, getName()+".minute");
|
||||||
|
String second = Globalization.decodeParameter(request, getName()+".second");
|
||||||
|
String amOrPm = Globalization.decodeParameter(request, getName()+".amOrPm");
|
||||||
|
|
||||||
|
// when submitting a non-compulsory datetime widget, we used to
|
||||||
|
// get an 'For input string: ""' error message; hence the datetime
|
||||||
|
// was compulsory anyways.
|
||||||
|
|
||||||
|
// check correctly that *something* was entered...
|
||||||
|
//if ( year == null && month == null && day == null ) {
|
||||||
|
if ((day == null || "".equals(day)) &&
|
||||||
|
(hour == null || "".equals(hour)) &&
|
||||||
|
(minute == null || "".equals(minute))) {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't just check nulls (which won't happen), but also ""
|
||||||
|
if ( year != null && !"".equals(year) ) {
|
||||||
|
c.set(Calendar.YEAR, Integer.parseInt(year));
|
||||||
|
}
|
||||||
|
if ( month != null && !"".equals(month) ) {
|
||||||
|
c.set(Calendar.MONTH, Integer.parseInt(month));
|
||||||
|
}
|
||||||
|
if ( day != null && !"".equals(day) ) {
|
||||||
|
c.set(Calendar.DATE, Integer.parseInt(day));
|
||||||
|
}
|
||||||
|
if ( hour != null && !"".equals(hour) ) {
|
||||||
|
c.set(Calendar.HOUR, Integer.parseInt(hour));
|
||||||
|
}
|
||||||
|
if ( minute != null && !"".equals(minute) ) {
|
||||||
|
c.set(Calendar.MINUTE, Integer.parseInt(minute));
|
||||||
|
}
|
||||||
|
if ( second != null && !"".equals(second) ) {
|
||||||
|
c.set(Calendar.SECOND, Integer.parseInt(second));
|
||||||
|
}
|
||||||
|
if ( amOrPm != null && !"".equals(amOrPm) ) {
|
||||||
|
c.set(Calendar.AM_PM, Integer.parseInt(amOrPm));
|
||||||
|
}
|
||||||
|
return c.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded) {
|
||||||
|
try {
|
||||||
|
return new Date(Long.parseLong(encoded));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot unmarshal dateTime '"
|
||||||
|
+ encoded +"': " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String marshal(Object value) {
|
||||||
|
return Long.toString(((Date) value).getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return Date.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents the model for number form parameters.
|
||||||
|
*
|
||||||
|
* @author Randy Graebner (randyg@alum.mit.edu)
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DoubleParameter extends NumberParameter {
|
||||||
|
|
||||||
|
public DoubleParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded) {
|
||||||
|
if( encoded == null || encoded.trim().length() == 0 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new Double(encoded);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException(getName() + " should be a " +
|
||||||
|
"Double Number, but is '" +
|
||||||
|
encoded + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return Double.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.arsdigita.bebop.parameters;
|
||||||
|
|
||||||
|
import javax.mail.internet.AddressException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import org.apache.oro.text.perl.Perl5Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An email address parameter. The parameter in the request is only
|
||||||
|
* accepted if it represents a valid email address such as
|
||||||
|
* <tt>webmaster@foo.com</tt>. The email address from the
|
||||||
|
* request is converted into a {@link
|
||||||
|
* javax.mail.internet.InternetAddress} if it looks like a valid email
|
||||||
|
* address. If it does not, the parameter flags a parameter validation
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* <p> The request value looks like a valid email address if it matches the
|
||||||
|
* regular expression
|
||||||
|
* <tt>^[^@<>\"\t ]+@[^@<>\".\t]+([.][^@<>\".\n]+)+$</tt>
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein
|
||||||
|
* @author Uday Mathur
|
||||||
|
* @author Rory Solomon
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class EmailParameter extends StringParameter {
|
||||||
|
|
||||||
|
private static Perl5Util re = new Perl5Util();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new email parameter corresponding to a request parameter
|
||||||
|
* with the given name.
|
||||||
|
*
|
||||||
|
* @param name the name of the request parameter from which the email
|
||||||
|
* address is read.
|
||||||
|
*/
|
||||||
|
public EmailParameter(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the email address from the request. Returns
|
||||||
|
* <code>null</code> if the request parameter does not look like a
|
||||||
|
* valid email address.
|
||||||
|
*
|
||||||
|
* @param request represents the current request
|
||||||
|
* @return the transformed email address as a {@link
|
||||||
|
* javax.mail.internet.InternetAddress} or <code>null</code> if
|
||||||
|
* there is no request parameter with the email parameter's name.
|
||||||
|
* @throws IllegalArgumentException if the request parameter does not
|
||||||
|
* look like a valid email address.
|
||||||
|
*/
|
||||||
|
public Object transformValue(HttpServletRequest request)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
return transformSingleValue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unmarshal(String encoded)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
// As stated above, if we get an invalid address just return null.
|
||||||
|
|
||||||
|
if (encoded == null || encoded.length() < 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
InternetAddress emailValue;
|
||||||
|
try {
|
||||||
|
emailValue = new InternetAddress(encoded);
|
||||||
|
} catch (AddressException e) {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
(getName() +
|
||||||
|
" is not a valid email address: '" + encoded + "'; " +
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// rogerh@arsdigita.com
|
||||||
|
// using InternetAddress constructor actually does very little error checking
|
||||||
|
// eg, new InternetAddress("blahblahblah") is considered a valid address
|
||||||
|
// thus we use good old ACS regex function here to valid email entery.
|
||||||
|
|
||||||
|
if (!isValidAddress(encoded)) {
|
||||||
|
//TODO: fix error display so html tags don't become quoted
|
||||||
|
throw new IllegalArgumentException("The email address that you typed " +
|
||||||
|
"doesn't look right to us. Examples of " +
|
||||||
|
"valid email addresses are: " +
|
||||||
|
"<ul>\n" +
|
||||||
|
" <li> Alice1234@aol.com\n" +
|
||||||
|
" <li> joe_smith@hp.com\n" +
|
||||||
|
" <li> pierre@inria.fr\n" +
|
||||||
|
"</ul>");
|
||||||
|
}
|
||||||
|
return emailValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getValueClass() {
|
||||||
|
return InternetAddress.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this is copy and pasted from com.arsdigita.kernel.acs.Utilities.
|
||||||
|
* to work around project dependency issue.
|
||||||
|
*/
|
||||||
|
private static boolean isValidAddress(String email) {
|
||||||
|
return re.match("/^[^@<>\"\t ]+@[^@<>\".\t]+([.][^@<>\".\n ]+)+$/", email);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue