Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: nextapp/echoservlet/OutgoingUpdateQueue.java


1   /* 
2    * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3    * Copyright (C) 2002-2004 NextApp, Inc.
4    *
5    * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6    *
7    * The contents of this file are subject to the Mozilla Public License Version
8    * 1.1 (the "License"); you may not use this file except in compliance with
9    * the License. You may obtain a copy of the License at
10   * http://www.mozilla.org/MPL/
11   *
12   * Software distributed under the License is distributed on an "AS IS" basis,
13   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14   * for the specific language governing rights and limitations under the
15   * License.
16   *
17   * Alternatively, the contents of this file may be used under the terms of
18   * either the GNU General Public License Version 2 or later (the "GPL"), or
19   * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20   * in which case the provisions of the GPL or the LGPL are applicable instead
21   * of those above. If you wish to allow use of your version of this file only
22   * under the terms of either the GPL or the LGPL, and not to allow others to
23   * use your version of this file under the terms of the MPL, indicate your
24   * decision by deleting the provisions above and replace them with the notice
25   * and other provisions required by the GPL or the LGPL. If you do not delete
26   * the provisions above, a recipient may use your version of this file under
27   * the terms of any one of the MPL, the GPL or the LGPL.
28   */
29  
30  package nextapp.echoservlet;
31  
32  import java.io.Serializable;
33  import java.util.HashSet;
34  import java.util.Iterator;
35  import java.util.Set;
36  
37  import nextapp.echo.Component;
38  
39  /**
40   * Manages outgoing updates to the client.
41   * 
42   * Managed updates include:
43   * <ul>
44   * <li>Panes requiring refresh</li>
45   * <li>Windows requiring refresh</li>
46   * <li>Windows requiring title update</li>
47   * <li>Windows requiring z-index adjustment (raising or lowering)</li>
48   * <li>Windows to be opened or closed</li>
49   * </ul>
50   */
51  final class OutgoingUpdateQueue 
52  implements Serializable {
53          
54      /**
55       * A set containing WindowUIs that need to be closed on the client.
56       */
57      private Set closedWindows = new HashSet();
58      
59      /**
60       * A set containing WindowUIs that need to be raised on the client.
61       */
62      private Set raisedWindows = new HashSet();
63      
64      /**
65       * A set containing WindowUIs that need to be lowered on the client.
66       */
67      private Set loweredWindows = new HashSet();
68      
69      /**
70       * The InstancePeer with which this OutgoingUpdateQueue is associated.
71       */
72      private InstancePeer instancePeer;
73      
74      /** 
75       * A set containing WindowUI's that need to be opened on the client.
76       */
77      private Set openedWindows = new HashSet();
78      
79      /**
80       * A set containing all services that need to be refreshed on the client.
81       */
82      private Set paneUpdates = new HashSet();
83      
84      /**
85       * A set containing all WindowUI's whose titles need to be updated on the 
86       * client.
87       */
88      private Set windowTitleUpdates = new HashSet();
89  
90      /**
91       * A set containing all WindowUI's whose content needs to be updated on 
92       * the client.
93       */
94      private Set windowUpdates = new HashSet();
95  
96      /**
97       * Creates a new OutgoingUpdateQueue for the specified InstancePeer.
98       * There should be one OutgoingUpdateQueue per InstancePeer.
99       *
100      * @param instancePeer The InstancePeer that this OutgoingUpdateQueue will handle 
101      *        updates for.
102      */
103     OutgoingUpdateQueue(InstancePeer instancePeer) {
104         super();
105         
106         this.instancePeer = instancePeer;
107     }
108     
109     /**
110      * Removes all previously scheduled service updates for services that
111      * are descendants of the specified <code>ComponentPeer</code>.  It is
112      * necessary to remove descendant services if the componentPeer has been
113      * removed or has become invisible.
114      *
115      * @param componentPeer The component peer whose descendant services
116      *        are to be removed.
117      */
118     void deleteComponentUpdate(ComponentPeer componentPeer) {
119         Component component = componentPeer.getComponent();
120         Component testComponent;
121         ComponentPeer testComponentPeer;
122         
123         for (Iterator it = paneUpdates.iterator(); it.hasNext();) {
124             testComponentPeer = (ComponentPeer) it.next();
125             testComponent = testComponentPeer.getComponent();
126             if (component.isAncestorOf(testComponent)) {
127                 it.remove();
128             }
129         }
130     }
131 
132     /**
133      * Returns all services (panes and windows) that are present on the client
134      * and need to be refreshed such that they are in sync with their 
135      * respective server-side representations.
136      *
137      * @return An iterator of services needing to be refreshed.
138      */
139     Iterator dequeuePaneUpdate() {
140         // Handle forwarding container panes: Any forwarding container pane found will have its first parent component that is not
141         // a forwarding container pnae redrawn instead.
142         Set forwardedContainerUpdates = null;
143         Iterator it = paneUpdates.iterator();
144         while (it.hasNext()) {
145             PaneUI paneUI = (PaneUI) it.next();
146             if (paneUI instanceof ContainerPaneUI && 
147                     !(((ContainerPaneUI) paneUI).getForwardingPane() instanceof ContainerPaneUI)) {
148                 // Pane update is a forwarding container pane, that is forwarding to a non-container pane: 
149                 // Update its parent instead.  This is done as otherwise the scrollbar policy desired by the pane being forwarded
150                 // to will not be observed.
151                 ComponentPeer componentPeer = (ComponentPeer) paneUI;
152                 
153                 // Find first parent component that is not a forwarding container pane.
154                 while (componentPeer instanceof ContainerPaneUI 
155                         && ((ContainerPaneUI) componentPeer).getForwardingPane() != componentPeer) {
156                     componentPeer = componentPeer.getParent();
157                 }
158     
159                 // Add peer to update list.
160                 if (forwardedContainerUpdates == null) {
161                     // lazy-create forwarded container update set
162                     forwardedContainerUpdates = new HashSet();
163                 }
164                 forwardedContainerUpdates.add(componentPeer);
165             }
166         }
167         if (forwardedContainerUpdates != null) {
168             it = forwardedContainerUpdates.iterator();
169             while (it.hasNext()) {
170                 enqueueComponentUpdate((ComponentPeer) it.next());
171             }
172         }
173     
174         return paneUpdates.iterator();
175     }
176     
177     /**
178      * Returns an iterator over all windows that are presently open on the 
179      * client and need to be closed.  The controller will use the 
180      * Iterator.remove() method to remove each window from this queue after
181      * it has generated script to close the window.
182      *
183      * @return An iterator of windows needing to be closed.
184      */    
185     Iterator dequeueWindowClose() {
186         return closedWindows.iterator();
187     }
188     
189     /**
190      * Returns an iterator over all windows that need to be lowered
191      * on the client.  The controller will use the
192      * Iterator.remove() method to remove each window from this queue after
193      * it has generated script to lower the window.
194      * 
195      * @return An iterator of windows needing to be lowered.
196      */
197     Iterator dequeueWindowLower() {
198         return loweredWindows.iterator();
199     }
200     
201     /**
202      * Returns an iterator over all windows that are presently closed on the 
203      * client and need to be opened.  The controller will use the 
204      * Iterator.remove() method to remove each window from this queue after
205      * it has generated script to open the window.
206      *
207      * @return An iterator of windows needing to be opened.
208      */    
209     Iterator dequeueWindowOpen() {
210         return openedWindows.iterator();
211     }
212     
213     /**
214      * Returns an iterator over all windows that need to be raised
215      * on the client.  The controller will use the 
216      * Iterator.remove() method to remove each window from this queue after
217      * it has generated script to raise the window.
218      *
219      * @return An iterator of windows needing to be raised.
220      */    
221     Iterator dequeueWindowRaise() {
222         return raisedWindows.iterator();
223     }
224     
225     /**
226      * Returns an iterator over all open windows that need to have their titles 
227      * updated on the client.
228      *
229      * @return An iterator of windows needing their titles updated.
230      */
231     Iterator dequeueWindowTitleUpdate() {
232         return windowTitleUpdates.iterator();
233     }
234     
235     /**
236      * Returns an iterator over all open windows that need to have their 
237      * content updated on the client.
238      *
239      * @return An iterator of windows needing their content updated.
240      */
241     Iterator dequeueWindowUpdate() {
242         return windowUpdates.iterator();
243     }
244 
245     /**
246      * Causes the service containing the given component to be re-rendered.
247      *
248      * @param componentPeer A <code>ComponentPeer</code> whose represented
249      *        <code>Component</code> has changed and therefore needs to be
250      *        updated on the client browser.
251      */
252     void enqueueComponentUpdate(ComponentPeer componentPeer) {
253         if (componentPeer instanceof WindowUI) {
254             enqueueWindowUpdate((WindowUI) componentPeer);
255         } else {
256             enqueuePaneUpdate(componentPeer);
257         }
258     }
259 
260     /**
261      * Adds the <code>Service</code> that is responsible for rendering the 
262      * specified <code>ComponentPeer</code> to the list of services needing
263      * to be refreshed.
264      *
265      * @param componentPeer The component that needs to be re-rendered.
266      */
267     private void enqueuePaneUpdate(ComponentPeer componentPeer) {
268         if (componentPeer instanceof WindowUI) {
269             throw new IllegalArgumentException("Cannot redraw window.");
270         }
271         
272         ComponentPeer parent = componentPeer.getParent();
273         if (parent instanceof WindowUI) {
274             // If the componentPeer is the root pane of the window, then forward the redraw request to
275             // redrawWindow().
276             enqueueWindowUpdate((WindowUI) parent);
277         } else {
278             PaneUI containingPane = instancePeer.getContainingPane(componentPeer);
279     
280             // Quick check to ensure that the service containing this component is not already queued for update.
281             // This check is done first because often many components under the same service will be updated at once.
282             if (paneUpdates.contains(containingPane)) {
283                 return;
284             }
285     
286             Iterator it;
287             Component testComponent;
288             Component component = componentPeer.getComponent();      
289     
290             // Determine if any services that are ancestors of the component are already scheduled for updating.
291             // At the same time, remove any descendant services of the component from being scheduled for updates. 
292             it = paneUpdates.iterator();
293             while (it.hasNext()) {
294                 testComponent = ((ComponentPeer) it.next()).getComponent();
295     
296                 // Check for ancestors of the test component.
297                 if (testComponent.isAncestorOf(component)) {
298                     return;
299                 }
300     
301                 if (component.isAncestorOf(testComponent)) {
302                     // A child of the component was found in the queue, therefore, the component should be added.  There is
303                     // no possiblility that the components parent could be in the queue.  Therefore, the iterator is now
304                     // used only to look for more child components.
305                     it.remove();
306                     while (it.hasNext()) {
307                         testComponent = ((ComponentPeer) it.next()).getComponent();
308                         if (component.isAncestorOf(testComponent)) {
309                             it.remove();
310                         }
311                     }
312                     paneUpdates.add(containingPane);
313                     return;
314                 }
315             }
316     
317             if (openedWindows.size() > 0) {
318                 // Determine if the component is a descendant of a window that is scheduled to be opened.
319                 // If this is the case, the component's containing service will not be scheduled for update.
320                 it = openedWindows.iterator();
321                 while (it.hasNext()) {
322                     testComponent = ((ComponentPeer) it.next()).getComponent();
323                     if (testComponent.isAncestorOf(component)) {
324                         // The window that contains the component is scheduled to be opened.  There is no reason
325                         // to schedule the component's containing service for an update.
326                         return;
327                     }
328                 }
329             }
330             
331             if (windowUpdates.size() > 0) {
332                 // Determine if the component is a descendant of a window that is scheduled to be refreshed.
333                 // If this is the case, the component's containing service will not be scheduled for update.
334                 it = windowUpdates.iterator();
335                 while (it.hasNext()) {
336                     testComponent = ((ComponentPeer) it.next()).getComponent();
337                     if (testComponent.isAncestorOf(component)) {
338                         // The window that contains the component is scheduled to be opened.  There is no reason
339                         // to schedule the component's containing service for an update.
340                         return;
341                     }
342                 }
343             }
344             
345             if (closedWindows.size() > 0) {
346                 // Determine if the component is a descendant of a window that is scheduled to be closed.
347                 // If this is the case, the component's containg service will not be scheduled for update.
348                 it = closedWindows.iterator();
349                 while (it.hasNext()) {
350                     testComponent = ((ComponentPeer) it.next()).getComponent();
351                     if (testComponent.isAncestorOf(component)) {
352                         // The window that contains the component is scheduled to be closed.  There is no reason
353                         // to schedule the component's containing service for an update.
354                         return;
355                     }
356                 }
357             }
358     
359             // The method has not prematurely returned, therefore no services that contain the component were found.
360             paneUpdates.add(containingPane);
361         }
362     }
363 
364     /**
365      * Removes a window from the application.  The window will be closed
366      * on the client browser if necessary (the only case where this is not
367      * necessary is if the window is not yet open but was scheduled to be 
368      * opened by a call to <code>addWindow()</code>).
369      *
370      * @param windowUI The window to be removed.
371      */
372     void enqueueWindowClose(WindowUI windowUI) {
373         if (openedWindows.contains(windowUI)) {
374             openedWindows.remove(windowUI);
375         } else {
376             closedWindows.add(windowUI);
377             windowUpdates.remove(windowUI);
378             windowTitleUpdates.remove(windowUI);
379             deleteComponentUpdate(windowUI);
380             loweredWindows.remove(windowUI);
381             raisedWindows.remove(windowUI);
382         }
383     }
384 
385     /** 
386      * Schedules a window to be lowered to the background of the client 
387      * application.
388      * 
389      * @param windowUI The window to lower.
390      */
391     void enqueueWindowLower(WindowUI windowUI) {
392         if (!openedWindows.contains(windowUI)) {
393             if (loweredWindows.contains(windowUI)) {
394                 loweredWindows.remove(windowUI);
395             }
396             if (raisedWindows.contains(windowUI)) {
397                 raisedWindows.remove(windowUI);
398             }
399             loweredWindows.add(windowUI);
400         }
401     }
402     
403     /**
404      * Adds a window to the application.  The window will be opened on the 
405      * client if necessary (the only case where this is not necessary is if
406      * the window is already opened and was scheduled to be closed by a call
407      * to <code>removeWindow()</code>).
408      *
409      * @param windowUI The window to be added.
410      */
411     void enqueueWindowOpen(WindowUI windowUI) {
412         if (closedWindows.contains(windowUI)) {
413             // The window was closed, and is being opened again, in the same step.  Therefore, the window on the user's screen, 
414             // The window is thus removed from the closedWindows collections. 
415             closedWindows.remove(windowUI);
416 
417             // The window may also need to be refreshed, and  because its state was not being monitored between the time it was
418             // closed and reopened, there is no way to tell  if an update is necessary.  Thus the window's content is marked to be
419             // updated.
420             windowUpdates.add(windowUI);
421             windowTitleUpdates.add(windowUI);
422         } else {
423             // The window needs to be opened on the client.  
424             openedWindows.add(windowUI);
425             
426             // Remove any service updates that are scheduled underneath the window.
427             deleteComponentUpdate(windowUI);
428             
429             // Remove the window from being updated as a service.
430             windowUpdates.remove(windowUI);
431             
432             // Remove any window title updates.
433             windowTitleUpdates.remove(windowUI);
434             
435             // Remove any window raising/lowering tasks
436             loweredWindows.remove(windowUI);
437             raisedWindows.remove(windowUI);
438         }
439     }
440     
441     /**
442      * Adds the specified <code>WindowUI</code> to the list of windows that
443      * need to have be raised on the client.
444      * 
445      * @param windowUI the peer of a <code>Window</code> which should be 
446      *        raised on the client.
447      */
448     void enqueueWindowRaise(WindowUI windowUI) {
449         if (!openedWindows.contains(windowUI)) {
450             if (loweredWindows.contains(windowUI)) {
451                 loweredWindows.remove(windowUI);
452             }
453             if (raisedWindows.contains(windowUI)) {
454                 raisedWindows.remove(windowUI);
455             }
456             raisedWindows.add(windowUI);
457         }
458     }
459 
460     /**
461      * Adds the specified <code>WindowUI</code> to the list of windows that
462      * need to have their titles updated.
463      *
464      * @param windowUI The peer of a <code>Window</code> whose title has been
465      *        changed.
466      */
467     void enqueueWindowTitleUpdate(WindowUI windowUI) {
468         if (!openedWindows.contains(windowUI) && !closedWindows.contains(windowUI)) {
469             windowTitleUpdates.add(windowUI);
470         }
471     }
472 
473     /**
474      * Adds the specified <code>WindowUI</code> to the list of windows that
475      * need to have their content updated.
476      *
477      * @param windowUI The peer of a <code>Window</code> whose content has been
478      *        changed.
479      */
480     private void enqueueWindowUpdate(WindowUI windowUI) {
481         // If the window is scheduled to be opened (and is therefore not yet open), return immediately.
482         if (openedWindows.contains(windowUI)) {
483             return;
484         }
485         
486         windowUpdates.add(windowUI);
487     }
488 
489     /**
490      * Returns true if there are services that need to be refreshed on the
491      * client.
492      *
493      * @return True if there are services that need to be refreshed on the
494      *         client.
495      */
496     boolean isPaneUpdateRequired() {
497         return paneUpdates.size() > 0;
498     }
499 
500     /**
501      * Returns true if there are windows on the client that need to be closed.
502      *
503      * @return True if there are windows on the client that need to be closed.
504      */
505     boolean isWindowCloseRequired() {
506         return closedWindows.size() > 0;
507     }
508 
509     /**
510      * Returns true if there are windows that need to be lowered to the 
511      * background of the screen.
512      * 
513      * @return True if there are windows that need to be lowered to the
514      *         background of the screen.
515      */
516     boolean isWindowLowerRequired() {
517         return loweredWindows.size() > 0;
518     }
519     
520     /**
521      * Returns true if there are windows that need to be opened on the client.
522      *
523      * @return True if there are windows that need to be opened on the client.
524      */
525     boolean isWindowOpenRequired() {
526         return openedWindows.size() > 0;
527     }
528     
529     /**
530      * Returns true if there are windows that need to be raised to the 
531      *         foreground of the screen.
532      * 
533      * @return True if there are windows that need to be raised to the 
534      *         foreground of the screen.
535      */
536     boolean isWindowRaiseRequired() {
537         return raisedWindows.size() > 0;
538     }
539     
540     /**
541      * Returns true if there are windows that need their titles updated on the 
542      * client.
543      *
544      * @return True if there are windows that need their titles updated on the 
545      *         client.
546      */
547     boolean isWindowTitleUpdateRequired() {
548         return windowTitleUpdates.size() > 0;
549     }
550     
551     /**
552      * Returns true if there are windows that need their content updated on 
553      * the client.
554      *
555      * @return True if there are windows that need their content updated on 
556      *         the client.
557      */
558     boolean isWindowUpdateRequired() {
559         return windowUpdates.size() > 0;
560     }
561     
562     /**
563      * Purges all updates.
564      */
565     void purgeAllUpdates() {
566         windowUpdates.clear();
567         windowTitleUpdates.clear();
568         paneUpdates.clear();
569         openedWindows.clear();
570         closedWindows.clear();
571         raisedWindows.clear();
572         loweredWindows.clear();
573     }
574 }