001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2004 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.app;
031:
032: import java.beans.PropertyChangeListener;
033: import java.beans.PropertyChangeSupport;
034: import java.io.Serializable;
035: import java.lang.ref.WeakReference;
036: import java.util.ArrayList;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.Locale;
041: import java.util.Map;
042:
043: import nextapp.echo2.app.update.ServerUpdateManager;
044: import nextapp.echo2.app.update.UpdateManager;
045: import nextapp.echo2.app.util.Uid;
046:
047: /**
048: * A single user-instance of an Echo application.
049: */
050: public abstract class ApplicationInstance implements Serializable {
051:
052: /** The name and version of the Echo API in use. */
053: public static final String ID_STRING = "NextApp Echo v2.1.0.rc2";
054:
055: public static final String FOCUSED_COMPONENT_CHANGED_PROPERTY = "focusedComponent";
056: public static final String LOCALE_CHANGED_PROPERTY = "locale";
057: public static final String MODAL_COMPONENTS_CHANGED_PROPERTY = "modalComponents";
058: public static final String WINDOWS_CHANGED_PROPERTY = "windows";
059:
060: /**
061: * A <code>ThreadLocal</code> reference to the
062: * <code>ApplicationInstance</code> relevant to the current thread.
063: */
064: private static final ThreadLocal activeInstance = new InheritableThreadLocal();
065:
066: /**
067: * Generates a system-level identifier (an identifier which is unique to all
068: * <code>ApplicationInstance</code>s).
069: *
070: * @return the generated identifier
071: * @see #generateId()
072: */
073: public static final String generateSystemId() {
074: return Uid.generateUidString();
075: }
076:
077: /**
078: * Returns a reference to the <code>ApplicationInstance</code> that is
079: * relevant to the current thread, or null if no instance is relevant.
080: *
081: * @return the relevant <code>ApplicationInstance</code>
082: */
083: public static final ApplicationInstance getActive() {
084: return (ApplicationInstance) activeInstance.get();
085: }
086:
087: /**
088: * Sets the <code>ApplicationInstance</code> that is relevant to the
089: * current thread. This method should be invoked with a null
090: * argument when the previously set <code>ApplicationInstance</code> is
091: * no longer relevant.
092: * <p>
093: * <b>This method should only be invoked by the application container.</b>
094: *
095: * @param applicationInstance the relevant <code>ApplicationInstance</code>
096: */
097: public static final void setActive(
098: ApplicationInstance applicationInstance) {
099: activeInstance.set(applicationInstance);
100: }
101:
102: /**
103: * The presently focused component.
104: */
105: private transient WeakReference focusedComponent;
106:
107: /**
108: * The default <code>Locale</code> of the application.
109: * This <code>Locale</code> will be inherited by <code>Component</code>s.
110: */
111: private Locale locale;
112:
113: /**
114: * The default <code>LayoutDirection</code> of the application, derived
115: * from the application's <code>Locale</code>.
116: * This <code>LayoutDirection</code> will be inherited by
117: * <code>Component</code>s.
118: */
119: private LayoutDirection layoutDirection;
120:
121: /**
122: * Contextual data.
123: * @see #getContextProperty(java.lang.String)
124: */
125: private Map context;
126:
127: /**
128: * Mapping from the render ids of all registered components to the
129: * <code>Component</code> instances themselves.
130: */
131: private Map renderIdToComponentMap;
132:
133: /**
134: * Mapping between <code>TaskQueueHandle</code>s and <code>List</code>s
135: * of <code>Runnable</code> tasks. Values may be null if a particular
136: * <code>TaskQueue</code> does not contain any tasks.
137: */
138: private HashMap taskQueueMap;
139:
140: /**
141: * Fires property change events for the instance object.
142: */
143: private PropertyChangeSupport propertyChangeSupport;
144:
145: /**
146: * The <code>UpdateManager</code> handling updates to/from this application.
147: */
148: private UpdateManager updateManager;
149:
150: /**
151: * The top-level <code>Window</code>.
152: * Currently only one top-level is supported per
153: * <code>ApplicationInstance</code>.
154: */
155: private Window defaultWindow;
156:
157: /**
158: * The <code>StyleSheet</code> used by the application.
159: */
160: private StyleSheet styleSheet;
161:
162: /**
163: * Collection of modal components, the last index representing the current
164: * modal context.
165: */
166: private List modalComponents;
167:
168: /**
169: * The next available sequentially generated
170: * <code>ApplicationInstance</code>-unique identifier value.
171: * @see #generateId()
172: */
173: private long nextId;
174:
175: /**
176: * Creates an <code>ApplicationInstance</code>.
177: */
178: public ApplicationInstance() {
179: super ();
180:
181: locale = Locale.getDefault();
182: layoutDirection = LayoutDirection.forLocale(locale);
183:
184: propertyChangeSupport = new PropertyChangeSupport(this );
185: updateManager = new UpdateManager(this );
186: renderIdToComponentMap = new HashMap();
187: taskQueueMap = new HashMap();
188: }
189:
190: /**
191: * Adds a <code>PropertyChangeListener</code> to receive notification of
192: * application-level property changes.
193: *
194: * @param l the listener to add
195: */
196: public void addPropertyChangeListener(PropertyChangeListener l) {
197: propertyChangeSupport.addPropertyChangeListener(l);
198: }
199:
200: /**
201: * Creates a new task queue. A handle object representing the created task
202: * queue is returned. The created task queue will remain active until it is
203: * provided to the <code>removeTaskQueue()</code> method. Developers must
204: * take care to invoke <code>removeTaskQueue()</code> on any created
205: * task queues.
206: *
207: * @return a <code>TaskQueueHandler</code> representing the created task
208: * queue
209: * @see #removeTaskQueue(TaskQueueHandle)
210: */
211: public TaskQueueHandle createTaskQueue() {
212: TaskQueueHandle taskQueue = new TaskQueueHandle() {
213: };
214: synchronized (taskQueueMap) {
215: taskQueueMap.put(taskQueue, null);
216: }
217: return taskQueue;
218: }
219:
220: /**
221: * Initializes the <code>ApplicationInstance</code>. This method is
222: * invoked by the application container.
223: *
224: * @return the default <code>Window</code> of the application
225: * @throws IllegalStateException in the event that the current thread is not
226: * permitted to update the state of the user interface
227: */
228: public final Window doInit() {
229: if (this != activeInstance.get()) {
230: throw new IllegalStateException(
231: "Attempt to update state of application user interface outside of user interface thread.");
232: }
233: Window window = init();
234: setDefaultWindow(window);
235: doValidation();
236: return window;
237: }
238:
239: /**
240: * Validates all components registered with the application.
241: */
242: public final void doValidation() {
243: doValidation(defaultWindow);
244: }
245:
246: /**
247: * Validates a single component and then recursively validates its
248: * children. This is the recursive support method for
249: * the parameterless <code>doValidation()</code> method.
250: *
251: * @param c The component to be validated.
252: * @see #doValidation()
253: */
254: private void doValidation(Component c) {
255: c.validate();
256: int size = c.getComponentCount();
257: for (int index = 0; index < size; ++index) {
258: doValidation(c.getComponent(index));
259: }
260: }
261:
262: /**
263: * Queues the given stateless <code>Command</code> for execution on the
264: * current client/server synchronization.
265: *
266: * @param command the <code>Command</code> to execute
267: */
268: public void enqueueCommand(Command command) {
269: updateManager.getServerUpdateManager().enqueueCommand(command);
270: }
271:
272: /**
273: * Enqueues a task to be run during the next client/server
274: * synchronization. The task will be run
275: * <b>synchronously</b> in the user interface update thread.
276: * Enqueuing a task in response to an external event will result
277: * in changes being pushed to the client.
278: *
279: * @param taskQueue the <code>TaskQueueHandle</code> representing the
280: * queue into which this task should be placed
281: * @param task the task to run on client/server synchronization
282: */
283: public void enqueueTask(TaskQueueHandle taskQueue, Runnable task) {
284: synchronized (taskQueueMap) {
285: List taskList = (List) taskQueueMap.get(taskQueue);
286: if (taskList == null) {
287: taskList = new ArrayList();
288: taskQueueMap.put(taskQueue, taskList);
289: }
290: taskList.add(task);
291: }
292: }
293:
294: /**
295: * Reports a bound property change.
296: *
297: * @param propertyName the name of the changed property
298: * @param oldValue the previous value of the property
299: * @param newValue the present value of the property
300: */
301: protected void firePropertyChange(String propertyName,
302: Object oldValue, Object newValue) {
303: propertyChangeSupport.firePropertyChange(propertyName,
304: oldValue, newValue);
305: }
306:
307: /**
308: * Generates an identifier which is unique within this
309: * <code>ApplicationInstance</code>. This identifier should not be
310: * used outside of the context of this <code>ApplicationInstance</code>.
311: *
312: * @return the unique identifier
313: * @see #generateSystemId()
314: */
315: public String generateId() {
316: return Long.toString(nextId++);
317: }
318:
319: /**
320: * Returns the value of a contextual property.
321: * Contextual properties are typically set by an application
322: * container, e.g., the Web Container, in order to provide
323: * container-specific information. The property names of contextual
324: * properties are provided within the application container
325: * documentation when their use is required.
326: *
327: * @param propertyName the name of the object
328: * @return the object
329: */
330: public Object getContextProperty(String propertyName) {
331: return context == null ? null : context.get(propertyName);
332: }
333:
334: /**
335: * Retrieves the component currently registered with the application
336: * with the specified render id.
337: *
338: * @param renderId the render id of the component
339: * @return the component (or null if no component with the specified
340: * render id is registered)
341: */
342: public Component getComponentByRenderId(String renderId) {
343: return (Component) renderIdToComponentMap.get(renderId);
344: }
345:
346: /**
347: * Returns the default window of the application.
348: *
349: * @return the default <code>Window</code>
350: */
351: public Window getDefaultWindow() {
352: return defaultWindow;
353: }
354:
355: /**
356: * Returns the presently focused component, if known.
357: *
358: * @return the focused component
359: */
360: public Component getFocusedComponent() {
361: if (focusedComponent == null) {
362: return null;
363: } else {
364: return (Component) focusedComponent.get();
365: }
366: }
367:
368: /**
369: * Returns the application instance's default
370: * <code>LayoutDirection</code>.
371: *
372: * @return the <code>Locale</code>
373: */
374: public LayoutDirection getLayoutDirection() {
375: return layoutDirection;
376: }
377:
378: /**
379: * Returns the application instance's default <code>Locale</code>.
380: *
381: * @return the <code>Locale</code>
382: */
383: public Locale getLocale() {
384: return locale;
385: }
386:
387: /**
388: * Retrieves the root component of the current modal context, or null
389: * if no modal context exists. Components which are not within the
390: * descendant hierarchy of the modal context are barred from receiving
391: * user input.
392: *
393: * @return the root component of the modal context
394: */
395: public Component getModalContextRoot() {
396: if (modalComponents == null || modalComponents.size() == 0) {
397: return null;
398: } else {
399: for (int i = modalComponents.size() - 1; i >= 0; --i) {
400: Component component = (Component) modalComponents
401: .get(i);
402: // Ignore invisible components.
403: if (component.isRenderVisible()) {
404: return component;
405: }
406: }
407: return null;
408: }
409: }
410:
411: /**
412: * Retrieves the style for the specified specified class of
413: * component / style name.
414: *
415: * @param componentClass the component <code>Class</code>
416: * @param styleName the component's specified style name
417: * @return the appropriate application-wide style, or null
418: * if none exists
419: */
420: public Style getStyle(Class componentClass, String styleName) {
421: if (styleSheet == null) {
422: return null;
423: } else {
424: return styleSheet.getStyle(componentClass, styleName);
425: }
426: }
427:
428: /**
429: * Retrieves the <code>UpdateManager</code> being used to manage the
430: * client/server synchronization of this <code>ApplicationInstance</code>
431: *
432: * @return the <code>UpdateManager</code>
433: */
434: public UpdateManager getUpdateManager() {
435: return updateManager;
436: }
437:
438: /**
439: * Determines if this <code>ApplicationInstance</code> currently has any
440: * active tasks queues, which might be monitoring external events.
441: *
442: * @return true if the instance has any task queues
443: */
444: public final boolean hasTaskQueues() {
445: return taskQueueMap.size() > 0;
446: }
447:
448: /**
449: * Determines if there are any queued tasks in any of the task
450: * queues associated with this <code>ApplicationInstance</code>.
451: * <p>
452: * This method may be overridden by an application in order to check
453: * on the status of long-running operations and enqueue tasks
454: * just-in-time. In such cases tasks should be <strong>enqueued</strong>
455: * and the value of <code>super.hasQueuedTasks()</code> should be
456: * returned. This method is not invoked by a user-interface thread and
457: * thus the component hierarchy may not be modified in
458: * overriding implementations.
459: *
460: * @return true if any tasks are queued
461: */
462: public boolean hasQueuedTasks() {
463: if (taskQueueMap.size() == 0) {
464: return false;
465: }
466: Iterator it = taskQueueMap.values().iterator();
467: while (it.hasNext()) {
468: List taskList = (List) it.next();
469: if (taskList != null && taskList.size() > 0) {
470: return true;
471: }
472: }
473: return false;
474: }
475:
476: /**
477: * Determines if the given component is modal (i.e., that only components
478: * below it in the hierarchy should be enabled).
479: *
480: * @param component the <code>Component</code>
481: * @return true if the <code>Component</code> is modal
482: */
483: private boolean isModal(Component component) {
484: return modalComponents != null
485: && modalComponents.contains(component);
486: }
487:
488: /**
489: * Invoked to initialize the application, returning the default window.
490: * The returned window must be visible.
491: *
492: * @return the default window of the application
493: */
494: public abstract Window init();
495:
496: /**
497: * Notifies the <code>UpdateManager</code> in response to a component
498: * property change or child addition/removal.
499: * <p>
500: * This method is invoked directly from <code>Component</code>s
501: * (rather than using a <code>PropertyChangeListener</code>) in the interest
502: * of memory efficiency.
503: *
504: * @param parent the parent/updated component
505: * @param propertyName the name of the property changed
506: * @param oldValue the previous value of the property
507: * (or the removed component in the case of a
508: * <code>CHILDREN_CHANGED_PROPERTY</code>)
509: * @param newValue the new value of the property
510: * (or the added component in the case of a
511: * <code>CHILDREN_CHANGED_PROPERTY</code>)
512: * @throws IllegalStateException in the event that the current thread is not
513: * permitted to update the state of the user interface
514: */
515: void notifyComponentPropertyChange(Component parent,
516: String propertyName, Object oldValue, Object newValue) {
517: // Ensure current thread is a user interface thread.
518: if (this != activeInstance.get()) {
519: throw new IllegalStateException(
520: "Attempt to update state of application user interface outside of user interface thread.");
521: }
522:
523: ServerUpdateManager serverUpdateManager = updateManager
524: .getServerUpdateManager();
525: if (Component.CHILDREN_CHANGED_PROPERTY.equals(propertyName)) {
526: if (newValue == null) {
527: serverUpdateManager.processComponentRemove(parent,
528: (Component) oldValue);
529: } else {
530: serverUpdateManager.processComponentAdd(parent,
531: (Component) newValue);
532: }
533: } else if (Component.PROPERTY_LAYOUT_DATA.equals(propertyName)) {
534: serverUpdateManager
535: .processComponentLayoutDataUpdate(parent);
536: } else if (Component.VISIBLE_CHANGED_PROPERTY
537: .equals(propertyName)) {
538: if (oldValue != null && newValue != null
539: && oldValue.equals(newValue)) {
540: return;
541: }
542: serverUpdateManager
543: .processComponentVisibilityUpdate(parent);
544: } else {
545: if (oldValue != null && newValue != null
546: && oldValue.equals(newValue)) {
547: return;
548: }
549: if (parent instanceof ModalSupport
550: && ModalSupport.MODAL_CHANGED_PROPERTY
551: .equals(propertyName)) {
552: setModal(parent, ((Boolean) newValue).booleanValue());
553: }
554: serverUpdateManager.processComponentPropertyUpdate(parent,
555: propertyName, oldValue, newValue);
556: }
557: }
558:
559: /**
560: * Processes client input specific to the <code>ApplicationInstance</code>
561: * received from the <code>UpdateManager</code>.
562: * Derivative implementations should take care to invoke
563: * <code>super.processInput()</code>.
564: */
565: public void processInput(String propertyName, Object propertyValue) {
566: if (FOCUSED_COMPONENT_CHANGED_PROPERTY.equals(propertyName)) {
567: setFocusedComponent((Component) propertyValue);
568: }
569: }
570:
571: /**
572: * Processes all queued tasks. This method may only be invoked from within a
573: * UI thread by the <code>UpdateManager</code>. Tasks are removed from queues
574: * once they have been processed.
575: */
576: public void processQueuedTasks() {
577: if (taskQueueMap.size() == 0) {
578: return;
579: }
580:
581: List currentTasks = new ArrayList();
582: synchronized (taskQueueMap) {
583: Iterator taskListsIt = taskQueueMap.values().iterator();
584: while (taskListsIt.hasNext()) {
585: List tasks = (List) taskListsIt.next();
586: if (tasks != null) {
587: currentTasks.addAll(tasks);
588: tasks.clear();
589: }
590: }
591: }
592: Iterator it = currentTasks.iterator();
593: while (it.hasNext()) {
594: ((Runnable) it.next()).run();
595: }
596: }
597:
598: /**
599: * Registers a component with the <code>ApplicationInstance</code>.
600: * The component will be assigned a unique render id in the event that
601: * it does not currently have one.
602: * <p>
603: * This method is invoked by <code>Component.setApplicationInstance()</code>
604: *
605: * @param component the component to register
606: * @see Component#register(ApplicationInstance)
607: */
608: void registerComponent(Component component) {
609: String renderId = component.getRenderId();
610: if (renderId == null
611: || renderIdToComponentMap.containsKey(renderId)) {
612: // Note that the render id is reassigned if it currently exists renderIdToComponentMap. This could be the case
613: // in the event a Component was being used in a pool.
614: component.assignRenderId(generateId());
615: }
616: renderIdToComponentMap.put(component.getRenderId(), component);
617: if (component instanceof ModalSupport
618: && ((ModalSupport) component).isModal()) {
619: setModal(component, true);
620: }
621: }
622:
623: /**
624: * Removes a <code>PropertyChangeListener</code> from receiving
625: * notification of application-level property changes.
626: *
627: * @param l the listener to remove
628: */
629: public void removePropertyChangeListener(PropertyChangeListener l) {
630: propertyChangeSupport.removePropertyChangeListener(l);
631: }
632:
633: /**
634: * Removes the task queue described the specified
635: * <code>TaskQueueHandle</code>.
636: *
637: * @param taskQueueHandle the <code>TaskQueueHandle</code> specifying the
638: * task queue to remove
639: * @see #createTaskQueue()
640: */
641: public void removeTaskQueue(TaskQueueHandle taskQueueHandle) {
642: synchronized (taskQueueMap) {
643: taskQueueMap.remove(taskQueueHandle);
644: }
645: }
646:
647: /**
648: * Sets a contextual property.
649: *
650: * @param propertyName the property name
651: * @param propertyValue the property value
652: *
653: * @see #getContextProperty(java.lang.String)
654: */
655: public void setContextProperty(String propertyName,
656: Object propertyValue) {
657: if (context == null) {
658: context = new HashMap();
659: }
660: if (propertyValue == null) {
661: context.remove(propertyName);
662: } else {
663: context.put(propertyName, propertyValue);
664: }
665: }
666:
667: /**
668: * Sets the default top-level window.
669: *
670: * @param window the default top-level window
671: */
672: private void setDefaultWindow(Window window) {
673: if (defaultWindow != null) {
674: throw new UnsupportedOperationException(
675: "Default window already set.");
676: }
677:
678: defaultWindow = window;
679: window.register(this );
680: firePropertyChange(WINDOWS_CHANGED_PROPERTY, null, window);
681: window.doInit();
682: }
683:
684: /**
685: * Sets the presently focused component.
686: *
687: * @param newValue the component to be focused
688: */
689: public void setFocusedComponent(Component newValue) {
690: if (newValue instanceof DelegateFocusSupport) {
691: newValue = ((DelegateFocusSupport) newValue)
692: .getFocusComponent();
693: }
694:
695: Component oldValue = getFocusedComponent();
696: if (newValue == null) {
697: focusedComponent = null;
698: } else {
699: focusedComponent = new WeakReference(newValue);
700: }
701: propertyChangeSupport.firePropertyChange(
702: FOCUSED_COMPONENT_CHANGED_PROPERTY, oldValue, newValue);
703: updateManager.getServerUpdateManager()
704: .processApplicationPropertyUpdate(
705: FOCUSED_COMPONENT_CHANGED_PROPERTY, oldValue,
706: newValue);
707: }
708:
709: /**
710: * Sets the default locale of the application.
711: *
712: * @param newValue the new locale
713: */
714: public void setLocale(Locale newValue) {
715: if (newValue == null) {
716: throw new IllegalArgumentException(
717: "ApplicationInstance Locale may not be null.");
718: }
719: Locale oldValue = locale;
720: locale = newValue;
721: layoutDirection = LayoutDirection.forLocale(locale);
722: propertyChangeSupport.firePropertyChange(
723: LOCALE_CHANGED_PROPERTY, oldValue, newValue);
724: updateManager.getServerUpdateManager().processFullRefresh();
725: }
726:
727: /**
728: * Sets the modal state of a component (i.e, whether only it and
729: * components below it in the hierarchy should be enabled).
730: *
731: * @param component the <code>Component</code>
732: * @param newValue the new modal state
733: */
734: private void setModal(Component component, boolean newValue) {
735: boolean oldValue = isModal(component);
736: if (newValue) {
737: if (modalComponents == null) {
738: modalComponents = new ArrayList();
739: }
740: if (!modalComponents.contains(component)) {
741: modalComponents.add(component);
742: }
743: } else {
744: if (modalComponents != null) {
745: modalComponents.remove(component);
746: }
747: }
748: firePropertyChange(MODAL_COMPONENTS_CHANGED_PROPERTY,
749: new Boolean(oldValue), new Boolean(newValue));
750: }
751:
752: /**
753: * Sets the <code>StyleSheet</code> of this
754: * <code>ApplicationInstance</code>. <code>Component</code>s
755: * registered with this instance will retrieve
756: * properties from the <code>StyleSheet</code>
757: * when property values are not specified directly
758: * in a <code>Component</code> or in its specified <code>Style</code>.
759: * <p>
760: * Note that setting the style sheet should be
761: * done sparingly, given that doing so forces the entire
762: * client state to be updated. Generally style sheets should
763: * only be reconfigured at application initialization and/or when
764: * the user changes the visual theme of a theme-capable application.
765: *
766: * @param styleSheet the new style sheet
767: */
768: public void setStyleSheet(StyleSheet styleSheet) {
769: this .styleSheet = styleSheet;
770: updateManager.getServerUpdateManager().processFullRefresh();
771: }
772:
773: /**
774: * Unregisters a component from the <code>ApplicationInstance</code>.
775: * <p>
776: * This method is invoked by <code>Component.setApplicationInstance()</code>.
777: *
778: * @param component the component to unregister
779: * @see Component#register(ApplicationInstance)
780: */
781: void unregisterComponent(Component component) {
782: renderIdToComponentMap.remove(component.getRenderId());
783: if (component instanceof ModalSupport
784: && ((ModalSupport) component).isModal()) {
785: setModal(component, false);
786: }
787: }
788:
789: /**
790: * Verifies that a <code>Component</code> is within the modal context,
791: * i.e., that if a modal <code>Component</code> is present, that it either
792: * is or is a child of that <code>Component</code>.
793: *
794: * @param component the <code>Component</code> to evaluate
795: * @return true if the <code>Component</code> is within the current
796: * modal context
797: * @see Component#verifyInput(java.lang.String, java.lang.Object)
798: */
799: boolean verifyModalContext(Component component) {
800: Component modalContextRoot = getModalContextRoot();
801: return modalContextRoot == null
802: || modalContextRoot.isAncestorOf(component);
803: }
804: }
|