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>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>oro</groupId>
|
||||
<artifactId>oro</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.jtidy</groupId>
|
||||
<artifactId>jtidy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<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