001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.component;
042:
043: import java.util.Iterator;
044: import java.util.Map;
045:
046: import javax.faces.component.ActionSource;
047: import javax.faces.component.UIComponent;
048: import javax.faces.context.FacesContext;
049: import javax.faces.event.AbortProcessingException;
050: import javax.faces.event.ActionEvent;
051: import javax.faces.event.ActionListener;
052: import javax.faces.event.FacesEvent;
053: import javax.faces.event.PhaseId;
054: import javax.faces.el.MethodBinding;
055:
056: import com.sun.rave.web.ui.el.DropDownMethodBinding;
057: import com.sun.rave.web.ui.util.ConversionUtilities;
058: import com.sun.rave.web.ui.util.RenderingUtilities;
059:
060: /**
061: * <p>Component that represents a drop down menu.</p>
062: */
063:
064: public class DropDown extends DropDownBase implements ActionSource {
065:
066: public final static String SUBMIT = "_submitter";
067: private boolean fireAction = false;
068:
069: private static final boolean DEBUG = false;
070:
071: // Override this because the generator screws up
072: public MethodBinding getAction() {
073:
074: if (DEBUG)
075: log("getAction()");
076:
077: if (super .getAction() == null && isNavigateToValue()) {
078: setAction(new DropDownMethodBinding());
079: }
080:
081: if (DEBUG)
082: log("\tAction is " + String.valueOf(super .getAction()));
083: return super .getAction();
084: }
085:
086: /**
087: * Getter for property Rows.
088: * @return Value of property Rows.
089: */
090: public int getRows() {
091:
092: return 1;
093: }
094:
095: /**
096: * Setter for property Rows.
097: * @param DisplayRows New value of property DisplayRows.
098: */
099: public void setRows(int DisplayRows) {
100:
101: super .setRows(1);
102: }
103:
104: /**
105: * Getter for property multiple
106: * @return Value of property multiple
107: */
108: public boolean getMultiple() {
109:
110: return false;
111: }
112:
113: /**
114: * Setter for property multiple
115: * @param multiple New value of property multiple
116: */
117: public void setMultiple(boolean multiple) {
118:
119: super .setMultiple(false);
120: }
121:
122: /**
123: * <p>Add a new {@link ActionListener} to the set of listeners interested
124: * in being notified when {@link ActionEvent}s occur.</p>
125: *
126: * @param listener The {@link ActionListener} to be added
127: *
128: * @exception NullPointerException if <code>listener</code>
129: * is <code>null</code>
130: */
131: public void addActionListener(ActionListener listener) {
132: // add the specified action listener
133: addFacesListener(listener);
134: }
135:
136: /**
137: * <p>Return the set of registered {@link ActionListener}s for this
138: * {@link ActionSource} instance. If there are no registered listeners,
139: * a zero-length array is returned.</p>
140: */
141: public ActionListener[] getActionListeners() {
142: // return all ActionListener instances associated with this component
143: ActionListener listeners[] = (ActionListener[]) getFacesListeners(ActionListener.class);
144: return listeners;
145: }
146:
147: /**
148: * <p>Remove an existing {@link ActionListener} (if any) from the set of
149: * listeners interested in being notified when {@link ActionEvent}s
150: * occur.</p>
151: *
152: * @param listener The {@link ActionListener} to be removed
153: *
154: * @exception NullPointerException if <code>listener</code>
155: * is <code>null</code>
156: */
157: public void removeActionListener(ActionListener listener) {
158: // remove the specified ActionListener from the list of listeners
159: removeFacesListener(listener);
160: }
161:
162: /**
163: * <p>The DropDown needs to override the standard decoding
164: * behaviour since it may also be an action source. We
165: * decode the component w.r.t. the value first, and
166: * validate it if the component is immediate. Then we
167: * fire an action event.</p>
168: * @exception NullPointerException
169: */
170: public void processDecodes(FacesContext context) {
171:
172: if (DEBUG)
173: log("processDecodes()");
174: // Skip processing if our rendered flag is false
175: if (!isRendered()) {
176: return;
177: }
178:
179: // Decode the component and any children
180: // Do we really want to decode any children?
181: // Process all facets and children of this component
182: Iterator childComponents = getFacetsAndChildren();
183: while (childComponents.hasNext()) {
184: UIComponent comp = (UIComponent) childComponents.next();
185: comp.processDecodes(context);
186: }
187:
188: // Get the value of this component
189: try {
190: decode(context);
191: } catch (RuntimeException e) {
192: context.renderResponse();
193: throw e;
194: }
195:
196: // Testing isSubmitter() alone is deceiving here.
197: // The component may have been the submitter but its
198: // submittedValue may still be null.
199: // This true in the case of an OptionTitle list option
200: // It submits but is treated as if there was no submit
201: // by not setting the submittedValue. It doesn't explicitly
202: // set it to null, in case the caller of the renderer decode
203: // method, set it to something special, for example if the
204: // DropDown is used as a sub component.
205: //
206: // However because this component did submit the form
207: // we still need to call setLastClientElement
208: // so after calling is submitter, still check submittedValue.
209: // and return if it is null.
210: //
211: // Also not that the submittedValue check has been added to
212: // validate, as in UIInput's validate method.
213: //
214: boolean isSubmitter = isSubmitter(context);
215:
216: if (isSubmitter) {
217: // since this component submitted the form, we need to make it have
218: // focus next time through. To do this, we will set an attribute
219: // in the request map.
220: RenderingUtilities.setLastClientID(context,
221: getPrimaryElementID(context));
222: }
223:
224: // Should we fire an action?
225: //
226: // Check submittedValue. Let the code fall through to
227: // validate for an immediate action and it will just return.
228: //
229: fireAction = isSubmitter && isSubmitForm()
230: && getSubmittedValue() != null;
231:
232: // If we are supposed to fire an action and navigate to the value
233: // of the component, we get the submitted value now and pass
234: // it to the DropDownMethodBinding.
235: if (fireAction && isNavigateToValue()) {
236: if (DEBUG)
237: log("\tHanding navigateToValue...");
238: MethodBinding mb = getAction();
239: if (DEBUG) {
240: if (mb != null)
241: log("\tMethod binding is " + mb.toString());
242: else
243: log("\tMethod binding is null");
244: }
245:
246: if (mb instanceof DropDownMethodBinding) {
247: String outcome = null;
248: Object values = getSubmittedValue();
249: if (values instanceof String[]) {
250: String[] stringValues = (String[]) values;
251: if (stringValues.length > 0) {
252: outcome = stringValues[0];
253: if (DEBUG)
254: log("Outcome is " + outcome);
255: }
256: }
257:
258: ((DropDownMethodBinding) mb).setValue(outcome);
259: if (DEBUG)
260: log("\tNavigate to " + outcome);
261: }
262: }
263:
264: // Next, if the component is immediate, we validate the component
265: if (isImmediate()) {
266: try {
267: validate(context);
268: } catch (RuntimeException e) {
269: context.renderResponse();
270: throw e;
271: }
272: if (!isValid()) {
273: context.renderResponse();
274: }
275: }
276: }
277:
278: /**
279: * <p>In addition to to the default {@link UIComponent#broadcast}
280: * processing, pass the {@link ActionEvent} being broadcast to the method
281: * referenced by <code>actionListener</code> (if any), and to the default
282: * {@link ActionListener} registered on the {@link Application}.</p>
283: *
284: * @param event {@link FacesEvent} to be broadcast
285: *
286: * @exception AbortProcessingException Signal the JavaServer Faces
287: * implementation that no further processing on the current event should be
288: * performed @exception IllegalArgumentException if the implementation class
289: * of this {@link FacesEvent} is not supported by this component
290: * @exception NullPointerException if <code>event</code> is
291: * <code>null</code>
292: */
293: public void broadcast(FacesEvent event)
294: throws AbortProcessingException {
295:
296: // Perform standard superclass processing
297: super .broadcast(event);
298:
299: if (isSubmitForm() && (event instanceof ActionEvent)) {
300: FacesContext context = getFacesContext();
301:
302: // Notify the specified action listener method (if any)
303: MethodBinding mb = getActionListener();
304: if (mb != null) {
305: mb.invoke(context, new Object[] { event });
306: }
307:
308: // Invoke the default ActionListener
309: ActionListener listener = context.getApplication()
310: .getActionListener();
311: if (listener != null) {
312: listener.processAction((ActionEvent) event);
313: }
314: }
315: }
316:
317: /**
318: * <p>Intercept <code>queueEvent</code> and, for {@link ActionEvent}s, mark
319: * the phaseId for the event to be <code>PhaseId.APPLY_REQUEST_VALUES</code>
320: * if the <code>immediate</code> flag is true,
321: * <code>PhaseId.INVOKE_APPLICATION</code> otherwise.</p>
322: */
323: public void queueEvent(FacesEvent e) {
324: // If this is an action event, we set the phase according to whether
325: // the component is immediate or not.
326: if (isSubmitForm()) {
327: if (e instanceof ActionEvent) {
328: if (isImmediate()) {
329: e.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
330: } else {
331: e.setPhaseId(PhaseId.INVOKE_APPLICATION);
332: }
333: }
334: }
335:
336: super .queueEvent(e);
337: }
338:
339: private boolean isSubmitter(FacesContext context) {
340:
341: if (DEBUG)
342: log("isSubmitter()");
343: String compID = getClientId(context).concat(SUBMIT);
344: Map requestParameters = context.getExternalContext()
345: .getRequestParameterMap();
346:
347: String submitter = (String) requestParameters.get(compID);
348: if (DEBUG)
349: log("\tValue of submitter field " + submitter);
350: return (submitter != null) ? submitter.equals("true") : false;
351: }
352:
353: public void validate(FacesContext context) {
354:
355: // From UIInput
356: //
357: // Submitted value == null means "the component was not submitted
358: // at all"; validation should not continue
359: //
360: Object submittedValue = getSubmittedValue();
361: if (submittedValue == null) {
362: return;
363: }
364:
365: super .validate(context);
366:
367: if (isValid() && fireAction) {
368: if (DEBUG)
369: log("\tQueue the component event");
370: // queue an event
371: queueEvent(new ActionEvent(this));
372: }
373: }
374: }
|