KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nextapp > echo2 > app > update > ServerUpdateManager


1 /*
2  * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3  * Copyright (C) 2002-2005 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.echo2.app.update;
31
32 import java.io.Serializable JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Collection JavaDoc;
36 import java.util.Comparator JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
40
41 import nextapp.echo2.app.ApplicationInstance;
42 import nextapp.echo2.app.Command;
43 import nextapp.echo2.app.Component;
44
45 /**
46  * Monitors updates to component hierarchy and records deltas between
47  * server state of application and client state of application.
48  */

49 public class ServerUpdateManager
50 implements Serializable JavaDoc {
51
52     /**
53      * <code>Comparator</code> to sort components by their depth in the
54      * hierarchy.
55      */

56     private static final Comparator JavaDoc hierarchyDepthUpdateComparator = new Comparator JavaDoc() {
57
58         /**
59          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
60          */

61         public int compare(Object JavaDoc a, Object JavaDoc b) {
62             return getDepth(((ServerComponentUpdate) a).getParent()) - getDepth(((ServerComponentUpdate) b).getParent());
63         }
64         
65         /**
66          * @see java.lang.Object#equals(java.lang.Object)
67          */

68         public boolean equals(Object JavaDoc o) {
69             return false;
70         }
71         
72         /**
73          * Returns the depth of the specified component in the hierarchy.
74          *
75          * @param component the component
76          * @return the depth
77          */

78         private int getDepth(Component component) {
79             int count = 0;
80             while (component != null) {
81                 component = component.getParent();
82                 ++count;
83             }
84             return count;
85         }
86     };
87     
88     private Map JavaDoc applicationUpdateMap;
89     private ArrayList JavaDoc commands;
90     private Map JavaDoc componentUpdateMap;
91     private ServerComponentUpdate fullRefreshUpdate;
92     private ClientUpdateManager clientUpdateManager;
93     private ApplicationInstance applicationInstance;
94     
95     /**
96      * Creates a new <code>ServerUpdateManager</code>.
97      *
98      * <strong>Warning:</strong> the <code>init()</code> method must be
99      * invoked before the <code>ServerUpdateManager</code> is used.
100      *
101      * @param applicationInstance the relevant <code>ApplicationInstance</code>
102      * @see #init(nextapp.echo2.app.update.ClientUpdateManager)
103      */

104     public ServerUpdateManager(ApplicationInstance applicationInstance) {
105         super();
106         this.applicationInstance = applicationInstance;
107         applicationUpdateMap = new HashMap JavaDoc();
108         commands = new ArrayList JavaDoc();
109         componentUpdateMap = new HashMap JavaDoc();
110         fullRefreshUpdate = new ServerComponentUpdate(null);
111     }
112     
113     /**
114      * Creates or retrieves a <code>ComponentUpdate</code> for the given
115      * parent component. If a <code>ComponentUpdate</code> is created, it
116      * is automatically stored in the update queue.
117      *
118      * @param parent the parent component of the update, i.e., the component
119      * which has had children added, removed, or its properties updated
120      * @return the created or retrieved update
121      */

122     private ServerComponentUpdate createComponentUpdate(Component parent) {
123         
124         ServerComponentUpdate update;
125         if (componentUpdateMap.containsKey(parent)) {
126             update = (ServerComponentUpdate) componentUpdateMap.get(parent);
127         } else {
128             update = new ServerComponentUpdate(parent);
129             componentUpdateMap.put(parent, update);
130         }
131         return update;
132     }
133     
134     /**
135      * Enqueues a <code>Command</code> for processing.
136      *
137      * @param command the command
138      */

139     public void enqueueCommand(Command command) {
140         commands.add(command);
141     }
142     
143     /**
144      * Returns a <code>PropertyUpdate</code> representing the
145      * application-level property update with the specified name.
146      * If the specified property has not been updated, null is returned.
147      *
148      * @param propertyName the name of the property
149      * @return the <code>PropertyUpdate</code>
150      */

151     public PropertyUpdate getApplicationPropertyUpdate(String JavaDoc propertyName) {
152         return (PropertyUpdate) applicationUpdateMap.get(propertyName);
153     }
154     
155     /**
156      * Returns the stored <code>Command</code>s. The commands
157      * are NOT removed or modified by this call.
158      *
159      * @return the commands
160      */

161     public Command[] getCommands() {
162         return (Command[])commands.toArray(new Command[commands.size()]);
163     }
164     
165     /**
166      * Returns the stored <code>ServerComponentUpdate</code>s. The updates
167      * are NOT removed or modified by this call. The updates will be returned
168      * sorted by depth of their parent components within the hierarchy, but in
169      * otherwise random order.
170      *
171      * @return the updates
172      */

173     public ServerComponentUpdate[] getComponentUpdates() {
174         if (isFullRefreshRequired()) {
175             return new ServerComponentUpdate[]{fullRefreshUpdate};
176         } else {
177             Collection JavaDoc hierarchyUpdates = componentUpdateMap.values();
178             ServerComponentUpdate[] serverComponentUpdates = (ServerComponentUpdate[])
179                      hierarchyUpdates.toArray(new ServerComponentUpdate[hierarchyUpdates.size()]);
180             Arrays.sort(serverComponentUpdates, hierarchyDepthUpdateComparator);
181             return serverComponentUpdates;
182         }
183     }
184     
185     /**
186      * Initialization life-cycle method. Must be invoked before using
187      * the <code>ServerUpdateManager</code>.
188      *
189      * @param clientUpdateManager the <code>ClientUpdateManager</code> that
190      * will be used to process input from the client
191      */

192     public void init(ClientUpdateManager clientUpdateManager) {
193         this.clientUpdateManager = clientUpdateManager;
194     }
195     
196     /**
197      * Determines if an ancestor of the given component is being added.
198      *
199      * @param component the <code>Component</code> to investigate
200      * @return true if an ancestor of the component is being added
201      */

202     private boolean isAncestorBeingAdded(Component component) {
203         Component child = component;
204         Component parent = component.getParent();
205         while (parent != null) {
206             ServerComponentUpdate update = (ServerComponentUpdate) componentUpdateMap.get(parent);
207             if (update != null) {
208                 if (update.hasAddedChild(child)) {
209                     return true;
210                 }
211             }
212             child = parent;
213             parent = parent.getParent();
214         }
215         return false;
216     }
217     
218     /**
219      * Determines if the manager has no updates.
220      *
221      * @return true if the manager has no updates
222      */

223     public boolean isEmpty() {
224         return componentUpdateMap.size() == 0;
225     }
226     
227     /**
228      * Determines if a full refresh of the client state is required.
229      *
230      * @return true if a full refresh is required
231      */

232     public boolean isFullRefreshRequired() {
233         return fullRefreshUpdate != null;
234     }
235     
236     /**
237      * Processes an update to a property of the <code>ApplicationInstance</code>.
238      *
239      * @param propertyName the name of the property
240      * @param oldValue the previous value of the property
241      * @param newValue the current value of the property
242      */

243     public void processApplicationPropertyUpdate(String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
244         Object JavaDoc clientValue = clientUpdateManager.getApplicationUpdatePropertyValue(propertyName);
245         if (clientValue == newValue || (clientValue != null && clientValue.equals(newValue))) {
246             // New value is same as client value, thus client is already in sync: cancel the update.
247
applicationUpdateMap.remove(propertyName);
248         } else {
249             applicationUpdateMap.put(propertyName, new PropertyUpdate(oldValue, newValue));
250         }
251     }
252     
253     /**
254      * Processes the addition of a component to the hierarchy.
255      * Creates/updates a <code>ServerComponentUpdate</code> if required.
256      *
257      * @param parent a component which currently exists in the hierarchy
258      * @param child the component which was added to <code>parent</code>
259      */

260     public void processComponentAdd(Component parent, Component child) {
261         if (isFullRefreshRequired()) {
262             return;
263         }
264         if (!child.isRenderVisible()) {
265             return;
266         }
267         if (isAncestorBeingAdded(child)) {
268             return;
269         }
270         
271         ServerComponentUpdate update = createComponentUpdate(parent);
272         update.addChild(child);
273     }
274     
275     /**
276      * Processes an update to the <code>LayoutData</code> of a component.
277      * Creates/updates a <code>ServerComponentUpdate</code> if required.
278      *
279      * @param updatedComponent a component which currently exists in the
280      * hierarchy whose <code>LayoutData</code> has changed
281      */

282     public void processComponentLayoutDataUpdate(Component updatedComponent) {
283         if (isFullRefreshRequired()) {
284             return;
285         }
286         if (!updatedComponent.isRenderVisible()) {
287             return;
288         }
289
290         Component parentComponent = updatedComponent.getParent();
291         if (parentComponent == null || isAncestorBeingAdded(parentComponent)) {
292             // Do nothing.
293
return;
294         }
295         ServerComponentUpdate update = createComponentUpdate(parentComponent);
296         update.updateLayoutData(updatedComponent);
297     }
298     
299     /**
300      * Processes an update to a property of a component (other than the
301      * <code>LayoutData</code> property).
302      * Creates/updates a <code>ServerComponentUpdate</code> if required.
303      *
304      * @param updatedComponent the component whose property(s) changed.
305      * @param propertyName the name of the changed property
306      * @param oldValue The previous value of the property
307      * @param newValue The new value of the property
308      */

309     public void processComponentPropertyUpdate(Component updatedComponent, String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
310         if (isFullRefreshRequired()) {
311             return;
312         }
313         if (!updatedComponent.isRenderVisible()) {
314             return;
315         }
316         if (isAncestorBeingAdded(updatedComponent)) {
317             return;
318         }
319         
320         // Do not add update (and if necessary cancel any update) if the property is being updated
321
// as the result of input from the client (and thus client and server state of property are
322
// already synchronized).
323
ClientComponentUpdate clientComponentUpdate = clientUpdateManager.getComponentUpdate(updatedComponent);
324         if (clientComponentUpdate != null) {
325             if (clientComponentUpdate.hasInput(propertyName)) {
326                 Object JavaDoc inputValue = clientComponentUpdate.getInputValue(propertyName);
327                 if (inputValue == newValue || (inputValue != null && inputValue.equals(newValue))) {
328                     ServerComponentUpdate update = (ServerComponentUpdate) componentUpdateMap.get(updatedComponent);
329                     if (update != null) {
330                         update.cancelUpdateProperty(propertyName);
331                     }
332                     return;
333                 }
334             }
335         }
336         
337         ServerComponentUpdate update = createComponentUpdate(updatedComponent);
338         update.updateProperty(propertyName, oldValue, newValue);
339     }
340
341     /**
342      * Processes the removal of a component from the hierarchy.
343      * Creates/updates a <code>ServerComponentUpdate</code> if required.
344      *
345      * @param parent a component which currently exists in the hierarchy
346      * @param child the component which was removed from <code>parent</code>
347      */

348     public void processComponentRemove(Component parent, Component child) {
349         if (isFullRefreshRequired()) {
350             return;
351         }
352         if (!parent.isRenderVisible()) {
353             return;
354         }
355         if (isAncestorBeingAdded(parent)) {
356             return;
357         }
358         ServerComponentUpdate update = createComponentUpdate(parent);
359         update.removeChild(child);
360         
361         // Search updated components for descendants of removed component.
362
// Any found descendants will be removed and added to this update's
363
// list of removed descendants.
364
Iterator JavaDoc it = componentUpdateMap.keySet().iterator();
365         while (it.hasNext()) {
366             Component testComponent = (Component) it.next();
367             if (child.isAncestorOf(testComponent)) {
368                 ServerComponentUpdate childUpdate = (ServerComponentUpdate) componentUpdateMap.get(testComponent);
369                 update.appendRemovedDescendants(childUpdate);
370                 it.remove();
371             }
372         }
373     }
374     
375     /**
376      * Processes an update to the visible state of a component.
377      * Creates/updates a <code>ServerComponentUpdate</code> if required.
378      *
379      * @param updatedComponent a component which currently exists in the
380      * hierarchy whose visible state has changed.
381      */

382     public void processComponentVisibilityUpdate(Component updatedComponent) {
383         Component parentComponent = updatedComponent.getParent();
384         if (updatedComponent.isVisible()) {
385             processComponentAdd(parentComponent, updatedComponent);
386         } else {
387             processComponentRemove(parentComponent, updatedComponent);
388         }
389     }
390     
391     /**
392      * Processes a full refresh of the application state, in response to a
393      * severe change, such as application locale or style sheet.
394      */

395     public void processFullRefresh() {
396         if (fullRefreshUpdate != null) {
397             return;
398         }
399         
400         fullRefreshUpdate = new ServerComponentUpdate(null);
401
402         if (applicationInstance.getDefaultWindow() != null) {
403             // Default window may be null if an operation is invoked from within the
404
// ApplicationInstsance.init() implementation that causes a full refresh.
405
fullRefreshUpdate.removeDescendant(applicationInstance.getDefaultWindow());
406         }
407
408         Iterator JavaDoc it = componentUpdateMap.keySet().iterator();
409         while (it.hasNext()) {
410             Component testComponent = (Component) it.next();
411             ServerComponentUpdate childUpdate = (ServerComponentUpdate) componentUpdateMap.get(testComponent);
412             fullRefreshUpdate.appendRemovedDescendants(childUpdate);
413             it.remove();
414         }
415     }
416     
417     /**
418      * Removes all <code>ServerComponentUpdate</code>s from the manager,
419      * resetting its state to zero. This method is invoked by the
420      * container once it has retrieved and processed all available updates.
421      */

422     void purge() {
423         applicationUpdateMap.clear();
424         componentUpdateMap.clear();
425         commands.clear();
426         fullRefreshUpdate = null;
427     }
428 }
429
Popular Tags