All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wings.SForm Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2000,2005 wingS development team.
 *
 * This file is part of wingS (http://wingsframework.org).
 *
 * wingS 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.
 *
 * Please see COPYING for the complete licence.
 */
package org.wings;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.plaf.FormCG;

import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.*;


/**
 * Container in which you need to wrap HTML input fields (ie. STextField)
 * to work correctly.
 * 

* The browser uses this object/tag to identify how (POST or GET) and where * to send an request originating from any input inside this form. *

* Note:Please be aware, that some components render differently if * placed inside a SForm. * * @author Armin Haaf */ public class SForm extends SContainer implements LowLevelEventListener { private final static Logger log = LoggerFactory.getLogger(SForm.class); /** * Default Form encoding type. See {@link #setEncodingType(String)}. */ public final static String ENC_TYPE_TEXT_PLAIN = "text/plain"; /** * Multipart form encoding. Needed for file uploads. See {@link #setEncodingType(String)}. */ public final static String ENC_TYPE_MULTIPART_FORM = "multipart/form-data"; /** * URL form encoding. See {@link #setEncodingType(String)}. */ public static final String URL_ENCODING = "application/x-www-form-urlencoded"; /** * Use method POST for submission of the data. */ private boolean postMethod = true; /** * EncondingType for submission of the data. */ private String encType; /** * Target URL to which data should be sent to */ private URL action; protected final EventListenerList listenerList = new EventListenerList(); protected String actionCommand; /** * the button, that is activated, if no other button is pressed in this * form. */ private SButton defaultButton; /** * the WingS event thread is the servlet doGet()/doPost() context * thread. Within this thread, we collect all armed components. A * 'armed' component is a component, that will 'fire' an event after the * first processRequest() stage is completed. */ private static ThreadLocal threadArmedComponents = new ThreadLocal() { @Override protected synchronized Object initialValue() { return new HashSet(2); } }; /** * Create a standard form component. */ public SForm() { } /** * Create a standard form component but redirects the request to the passed * URL. Use this i.e. to address other servlets. * * @param action The target URL. */ public SForm(URL action) { setAction(action); } /** * Create a standard form component. * * @param layout The layout to apply to this container. * @see SContainer */ public SForm(SLayoutManager layout) { super(layout); } /** * A SForm fires an event each time it was triggered (i.e. pressing asubmit button inside) * * @param actionCommand The action command to place insiside the {@link ActionEvent} */ public void setActionCommand(String actionCommand) { String oldVal = this.actionCommand; this.actionCommand = actionCommand; propertyChangeSupport.firePropertyChange("actionCommand", oldVal, this.actionCommand); } /** * @see #setActionCommand(String) */ public String getActionCommand() { return actionCommand; } /** * Set the default button activated upon enter. * The button is triggered if you press enter inside a form to submit it. * @param defaultButton A button which will be rendered invisible. * If null enter key pressed will be catched by the wings framework. */ public void setDefaultButton(SButton defaultButton) { SButton oldButton = this.defaultButton; String oldName = oldButton != null ? oldButton.getName() : null; String newName = defaultButton != null ? defaultButton.getName() : null; if (isDifferent(oldName, newName)) update(getCG().getDefaultButtonNameUpdate(this, newName)); this.defaultButton = defaultButton; propertyChangeSupport.firePropertyChange("defaultButton", oldButton, this.defaultButton); } /** * @see #setDefaultButton(SButton) */ public SButton getDefaultButton() { return this.defaultButton; } /** * Add a listener for Form events. A Form event is always triggered, when * a form has been submitted. Usually, this happens, whenever a submit * button is pressed or some other mechanism triggered the posting of the * form. Other mechanisms are *

    *
  • Java Script submit() event
  • *
  • If a form contains a single text input, then many browsers * submit the form, if the user presses RETURN in that field. In that * case, the submit button will not receive any event but * only the form. *
  • The {@link SFileChooser} will trigger a form event, if the file * size exceeded the allowed size. In that case, even if the submit * button has been pressed, no submit-button event will be triggered. * (For details, see {@link SFileChooser}). *
* Form events are guaranteed to be triggered after all * Selection-Changes and Button ActionListeners. */ public void addActionListener(ActionListener listener) { listenerList.add(ActionListener.class, listener); } /** * Remove a form action listener, that has been added in * {@link #addActionListener(ActionListener)} */ public void removeActionListener(ActionListener listener) { listenerList.remove(ActionListener.class, listener); } /** * Fire a ActionEvent at each registered listener. */ protected void fireActionPerformed(String pActionCommand) { ActionEvent e = null; // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ActionListener.class) { // lazy create ActionEvent if (e == null) { e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, pActionCommand); } ((ActionListener) listeners[i + 1]).actionPerformed(e); } } } /** * Register a components to be subject to fire component events in a later phase of * the request processing. SForm will call * {@link org.wings.LowLevelEventListener#fireIntermediateEvents()} and later * {@link org.wings.LowLevelEventListener#fireFinalEvents()} in a later phase of * the request. The calls on the components will be ordered dependend on their type. * * @param component The component to callback for event firing in a later phase of the request * @see #fireEvents() */ public static void addArmedComponent(LowLevelEventListener component) { Set armedComponents = (Set) threadArmedComponents.get(); armedComponents.add(component); } /** * clear armed components. This is usually not necessary, since sessions * clear clear their armed components. But if there was some Exception, it * might well be, that this does not happen. */ public static void clearArmedComponents() { Set armedComponents = (Set) threadArmedComponents.get(); armedComponents.clear(); } /* * Die Sache muss natuerlich Thread Save sein, d.h. es duerfen nur * die Events gefeuert werden, die auch aus dem feuernden Thread * stammen (eben dem Dispatcher Thread). Sichergestellt wird das * dadurch das beim abfeuern der Event in eine Queue (ArrayList) * gestellt wird, die zu dem feuernden Event gehoert. Diese Queues * der verschiedenen Threads werden in einer Map verwaltet. * Beim feuern wird dann die Queue, die dem aktuellen Thread * entspricht gefeuert und aus der Map entfernt. */ /** * This method fires the low level events for all "armed" components of * this thread (http session) in an ordered manner: *
  • forms *
  • buttons / clickables *
  • "regular" components
* This order derives out of the assumption, that a user first modifies * regular components before he presses the button submitting his changes. * Otherwise button actions would get fired before the edit components * fired their events. */ public static void fireEvents() { Set armedComponents = (Set) threadArmedComponents.get(); // use a copy to avoid concurrent modification exceptions if a // LowLevelEventListener adds itself to the armedComponents again try { // handle form special, form event should be fired last // hopefully there is only one form ;-) Set armedComponentsCopy = new HashSet(armedComponents); Iterator iterator = armedComponentsCopy.iterator(); LinkedList formEvents = null; LinkedList buttonEvents = null; while (iterator.hasNext()) { LowLevelEventListener component = (LowLevelEventListener) iterator.next(); /* fire form events at last * there could be more than one form event (e.g. mozilla posts a * hidden element even if it is in a form outside the posted * form (if the form is nested). Forms should not be nested in HTML. */ if (component instanceof SForm) { if (formEvents == null) { formEvents = new LinkedList(); } // end of if () formEvents.add(component); iterator.remove(); } else if (component instanceof SAbstractIconTextCompound) { if (buttonEvents == null) { buttonEvents = new LinkedList(); } buttonEvents.add(component); iterator.remove(); } else { component.fireIntermediateEvents(); } } /* * no buttons in forms pressed ? Then consider the default-Button. */ // Wrong - this fires default button for page scrollers! /*if (buttonEvents == null && formEvents != null) { Iterator fit = formEvents.iterator(); while (fit.hasNext()) { SForm form = (SForm) fit.next(); SButton defaultButton = form.getDefaultButton(); if (defaultButton != null) { if (buttonEvents == null) { buttonEvents = new LinkedList(); } buttonEvents.add(defaultButton); } } } */ if (buttonEvents != null) { iterator = buttonEvents.iterator(); while (iterator.hasNext()) { ((SAbstractIconTextCompound) iterator.next()).fireIntermediateEvents(); } } if (formEvents != null) { iterator = formEvents.iterator(); while (iterator.hasNext()) { ((SForm) iterator.next()).fireIntermediateEvents(); } } iterator = armedComponentsCopy.iterator(); while (iterator.hasNext()) { LowLevelEventListener component = (LowLevelEventListener) iterator.next(); // fire form events at last component.fireFinalEvents(); } if (buttonEvents != null) { iterator = buttonEvents.iterator(); while (iterator.hasNext()) { ((SAbstractIconTextCompound) iterator.next()).fireFinalEvents(); } buttonEvents.clear(); } if (formEvents != null) { iterator = formEvents.iterator(); while (iterator.hasNext()) { ((SForm) iterator.next()).fireFinalEvents(); } formEvents.clear(); } } finally { armedComponents.clear(); } } /** * Set, whether this form is to be transmitted via POST (true) * or GET (false). The default, and this is what you * usually want, is POST. */ public void setPostMethod(boolean postMethod) { boolean oldVal = this.postMethod; if (isDifferent(this.postMethod, postMethod)) update(getCG().getMethodUpdate(this, postMethod ? "post" : "get")); this.postMethod = postMethod; propertyChangeSupport.firePropertyChange("postMethod", oldVal, this.postMethod); } /** * Returns, whether this form is transmitted via POST (true) * or GET (false).

* Default is true. * * @return true if form postedt via POST, * false if via GET (false). */ public boolean isPostMethod() { return postMethod; } /** * Set the encoding of this form. This actually is an HTML interna * that bubbles up here. By default, the encoding type of any HTML-form * is application/x-www-form-urlencoded, and as such, needn't * be explicitly set with this setter. However, if you've included a * file upload element (as represented by {@link SFileChooser}) in your * form, this must be set to multipart/form-data, since only * then, files are transmitted correctly. In 'normal' forms without * file upload, it is not necessary to set it to * multipart/form-data; actually it enlarges the data to * be transmitted, so you probably don't want to do this, then. * * @param type the encoding type; one of multipart/form-data, * application/x-www-form-urlencoded or null to detect encoding. */ public void setEncodingType(String type) { String oldVal = this.encType; if (isDifferent(encType, type)) update(getCG().getEncodingUpdate(this, type)); encType = type; propertyChangeSupport.firePropertyChange("encodingType", oldVal, this.encType); } /** * Get the current encoding type, as set with * {@link #setEncodingType(String)}. If no encoding type was set, this * method detects the best encoding type. This can be expensive, so if * you can, set the encoding type. * * @return string containing the encoding type. This is something like * multipart/form-data, * application/x-www-form-urlencoded .. or 'null' * by default. */ public String getEncodingType() { return encType; } int fileChooserCount; public void registerFileChooser(SFileChooser fileChooser) { fileChooserCount++; if (!ENC_TYPE_MULTIPART_FORM.equals(encType)) setEncodingType(ENC_TYPE_MULTIPART_FORM); } public void unregisterFileChooser(SFileChooser fileChooser) { fileChooserCount--; if (fileChooserCount == 0 && ENC_TYPE_MULTIPART_FORM.equals(encType)) setEncodingType(ENC_TYPE_TEXT_PLAIN); } public void setAction(URL action) { URL oldVal = this.action; this.action = action; propertyChangeSupport.firePropertyChange("action", oldVal,this. action); } public URL getAction() { return action; } @Override public RequestURL getRequestURL() { RequestURL addr = super.getRequestURL(); if (action != null) { addr.addParameter(action.toString()); // ?? } return addr; } @Override public void processLowLevelEvent(String action, String... values) { processKeyEvents(values); if (action.endsWith("_keystroke")) return; // we have to wait, until all changed states of our form have // changed, before we anything can happen. SForm.addArmedComponent(this); } @Override public void fireIntermediateEvents() { } @Override public void fireFinalEvents() { fireKeyEvents(); fireActionPerformed(actionCommand); } /** @see LowLevelEventListener#isEpochCheckEnabled() */ private boolean epochCheckEnabled = true; /** @see LowLevelEventListener#isEpochCheckEnabled() */ @Override public boolean isEpochCheckEnabled() { return epochCheckEnabled; } /** @see LowLevelEventListener#isEpochCheckEnabled() */ public void setEpochCheckEnabled(boolean epochCheckEnabled) { boolean oldVal = this.epochCheckEnabled; this.epochCheckEnabled = epochCheckEnabled; propertyChangeSupport.firePropertyChange("epochCheckEnabled", oldVal, this.epochCheckEnabled); } @Override public SComponent addComponent(SComponent c, Object constraint, int index) { if (c instanceof SForm) log.warn("WARNING: attempt to nest forms; won't work."); return super.addComponent(c, constraint, index); } public void setCG(FormCG cg) { super.setCG(cg); } @Override public FormCG getCG() { return (FormCG)super.getCG(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy