KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nextapp > echo2 > webcontainer > syncpeer > RowPeer


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.webcontainer.syncpeer;
31
32 import org.w3c.dom.Document JavaDoc;
33 import org.w3c.dom.DocumentFragment JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35 import org.w3c.dom.Node JavaDoc;
36
37 import nextapp.echo2.app.Alignment;
38 import nextapp.echo2.app.Border;
39 import nextapp.echo2.app.Color;
40 import nextapp.echo2.app.Component;
41 import nextapp.echo2.app.Extent;
42 import nextapp.echo2.app.Font;
43 import nextapp.echo2.app.ImageReference;
44 import nextapp.echo2.app.Insets;
45 import nextapp.echo2.app.Row;
46 import nextapp.echo2.app.LayoutData;
47 import nextapp.echo2.app.layout.RowLayoutData;
48 import nextapp.echo2.app.update.ServerComponentUpdate;
49 import nextapp.echo2.webcontainer.ContainerInstance;
50 import nextapp.echo2.webcontainer.DomUpdateSupport;
51 import nextapp.echo2.webcontainer.PartialUpdateManager;
52 import nextapp.echo2.webcontainer.RenderContext;
53 import nextapp.echo2.webcontainer.RenderState;
54 import nextapp.echo2.webcontainer.ComponentSynchronizePeer;
55 import nextapp.echo2.webcontainer.SynchronizePeerFactory;
56 import nextapp.echo2.webcontainer.image.ImageRenderSupport;
57 import nextapp.echo2.webcontainer.partialupdate.BorderUpdate;
58 import nextapp.echo2.webcontainer.partialupdate.ColorUpdate;
59 import nextapp.echo2.webcontainer.partialupdate.InsetsUpdate;
60 import nextapp.echo2.webcontainer.propertyrender.AlignmentRender;
61 import nextapp.echo2.webcontainer.propertyrender.BorderRender;
62 import nextapp.echo2.webcontainer.propertyrender.CellLayoutDataRender;
63 import nextapp.echo2.webcontainer.propertyrender.ColorRender;
64 import nextapp.echo2.webcontainer.propertyrender.ExtentRender;
65 import nextapp.echo2.webcontainer.propertyrender.FontRender;
66 import nextapp.echo2.webcontainer.propertyrender.InsetsRender;
67 import nextapp.echo2.webrender.output.CssStyle;
68 import nextapp.echo2.webrender.servermessage.DomUpdate;
69
70 /**
71  * Synchronization peer for <code>nextapp.echo2.app.Row</code> components.
72  * <p>
73  * This class should not be extended or used by classes outside of the
74  * Echo framework.
75  */

76 public class RowPeer
77 implements ComponentSynchronizePeer, DomUpdateSupport, ImageRenderSupport {
78
79     /**
80      * <code>RenderState</code> implementation.
81      */

82     private static class RowPeerRenderState
83     implements RenderState {
84         
85         /**
86          * The child <code>Component</code> which had the highest index during
87          * the last rendering. This information is necessary when rendering
88          * cell spacing, as the last component will not have a "spacing" row
89          * beneath it. Thus, if it is no longer the last component due to an
90          * add, one will need to be added beneath it.
91          */

92         public Component lastChild;
93     }
94     
95     private PartialUpdateManager partialUpdateManager;
96     
97     /**
98      * Default constructor.
99      */

100     public RowPeer() {
101         partialUpdateManager = new PartialUpdateManager();
102         partialUpdateManager.add(Row.PROPERTY_BORDER, new BorderUpdate(Row.PROPERTY_BORDER, null,
103                 BorderUpdate.CSS_BORDER));
104         partialUpdateManager.add(Row.PROPERTY_FOREGROUND, new ColorUpdate(Row.PROPERTY_FOREGROUND, null,
105                 ColorUpdate.CSS_COLOR));
106         partialUpdateManager.add(Row.PROPERTY_BACKGROUND, new ColorUpdate(Row.PROPERTY_BACKGROUND, null,
107                 ColorUpdate.CSS_BACKGROUND_COLOR));
108         partialUpdateManager.add(Row.PROPERTY_INSETS, new InsetsUpdate(Row.PROPERTY_INSETS, null,
109                 InsetsUpdate.CSS_PADDING));
110     }
111     
112     /**
113      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#getContainerId(nextapp.echo2.app.Component)
114      */

115     public String JavaDoc getContainerId(Component child) {
116         return ContainerInstance.getElementId(child.getParent()) + "_cell_" + ContainerInstance.getElementId(child);
117     }
118     
119     /**
120      * @see nextapp.echo2.webcontainer.image.ImageRenderSupport#getImage(nextapp.echo2.app.Component, java.lang.String)
121      */

122     public ImageReference getImage(Component component, String JavaDoc imageId) {
123         // Only source of ImageReferences from this component are CellLayoutData background images:
124
// Delegate work to CellLayoutDataRender convenience method.
125
return CellLayoutDataRender.getCellLayoutDataBackgroundImage(component, imageId);
126     }
127
128     /**
129      * Returns the <code>RowLayoutData</code> of the given child, or null if
130      * it does not provide layout data.
131      *
132      * @param child the child component
133      * @return the layout data
134      * @throws java.lang.RuntimeException if the the provided
135      * <code>LayoutData</code> is not a <code>RowLayoutData</code>
136      */

137     private RowLayoutData getLayoutData(Component child) {
138         LayoutData layoutData = (LayoutData) child.getRenderProperty(Component.PROPERTY_LAYOUT_DATA);
139         if (layoutData == null) {
140             return null;
141         } else if (layoutData instanceof RowLayoutData) {
142             return (RowLayoutData) layoutData;
143         } else {
144             throw new RuntimeException JavaDoc("Invalid LayoutData for Row Child: " + layoutData.getClass().getName());
145         }
146     }
147
148     /**
149      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderAdd(nextapp.echo2.webcontainer.RenderContext,
150      * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String, nextapp.echo2.app.Component)
151      */

152     public void renderAdd(RenderContext rc, ServerComponentUpdate update, String JavaDoc targetId, Component component) {
153         Element JavaDoc domAddElement = DomUpdate.renderElementAdd(rc.getServerMessage());
154         DocumentFragment JavaDoc htmlFragment = rc.getServerMessage().getDocument().createDocumentFragment();
155         renderHtml(rc, update, htmlFragment, component);
156         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, targetId, htmlFragment);
157     }
158     
159     /**
160      * Renders a child component.
161      *
162      * @param rc the relevant <code>RenderContext</code>
163      * @param update the update
164      * @param parentElement the HTML element which should contain the child
165      * @param child the child component to render
166      */

167     private void renderAddChild(RenderContext rc, ServerComponentUpdate update, Element JavaDoc parentElement, Component child) {
168         ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(child.getClass());
169         if (syncPeer instanceof DomUpdateSupport) {
170             ((DomUpdateSupport) syncPeer).renderHtml(rc, update, parentElement, child);
171         } else {
172             syncPeer.renderAdd(rc, update, getContainerId(child), child);
173         }
174     }
175     
176     /**
177      * Renders child components which were added to a <code>Row</code>, as
178      * described in the provided <code>ServerComponentUpdate</code>.
179      *
180      * @param rc the relevant <code>RenderContext</code>
181      * @param update the update
182      */

183     private void renderAddChildren(RenderContext rc, ServerComponentUpdate update) {
184         Element JavaDoc domAddElement = DomUpdate.renderElementAdd(rc.getServerMessage());
185         Row row = (Row) update.getParent();
186         String JavaDoc elementId = ContainerInstance.getElementId(row);
187         String JavaDoc trElementId = elementId + "_tr";
188         
189         Component[] components = update.getParent().getVisibleComponents();
190         Component[] addedChildren = update.getAddedChildren();
191         
192         for (int componentIndex = components.length - 1; componentIndex >= 0; --componentIndex) {
193             boolean childFound = false;
194             for (int addedChildrenIndex = 0; !childFound && addedChildrenIndex < addedChildren.length; ++addedChildrenIndex) {
195                 if (addedChildren[addedChildrenIndex] == components[componentIndex]) {
196                     DocumentFragment JavaDoc htmlFragment = rc.getServerMessage().getDocument().createDocumentFragment();
197                     renderChild(rc, update, htmlFragment, row, components[componentIndex]);
198                     if (componentIndex == components.length - 1) {
199                         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, trElementId, htmlFragment);
200                     } else {
201                         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, trElementId,
202                                 elementId + "_cell_" + ContainerInstance.getElementId(components[componentIndex + 1]),
203                                 htmlFragment);
204                     }
205                     childFound = true;
206                 }
207             }
208         }
209         
210         // Special case: Recall the child which was rendered at the last index of the row on the previous
211
// rendering. If this child is still present but is no longer at the last index, render a spacing
212
// cell after it (if necessary).
213
RowPeerRenderState renderState = (RowPeerRenderState) rc.getContainerInstance().getRenderState(row);
214         if (renderState != null && renderState.lastChild != null) {
215             int previousLastChildIndex = row.visibleIndexOf(renderState.lastChild);
216             if (previousLastChildIndex != -1 && previousLastChildIndex != row.getVisibleComponentCount() - 1) {
217                 // At this point it is known that the child which was previously last is present, but is no longer last.
218

219                 // In the event the child was removed and re-added, the special case is unnecessary.
220
boolean lastChildMoved = false;
221                 for (int i = 0; i < addedChildren.length; ++i) {
222                     if (renderState.lastChild == addedChildren[i]) {
223                         lastChildMoved = true;
224                     }
225                 }
226                 if (!lastChildMoved) {
227                     DocumentFragment JavaDoc htmlFragment = rc.getServerMessage().getDocument().createDocumentFragment();
228                     renderSpacingCell(htmlFragment, row, renderState.lastChild);
229                     DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, trElementId,
230                             elementId + "_cell_" + ContainerInstance.getElementId(components[previousLastChildIndex + 1]),
231                             htmlFragment);
232                 }
233             }
234         }
235     }
236     
237     /**
238      * Renders an individual child component of the <code>Row</code>.
239      *
240      * @param rc the relevant <code>RenderContext</code>
241      * @param update the <code>ServerComponentUpdate</code> being performed
242      * @param parentNode the containing node to which the child should be
243      * appended
244      * @param child The child <code>Component</code> to be rendered
245      */

246     private void renderChild(RenderContext rc, ServerComponentUpdate update, Node JavaDoc parentNode,
247             Component component, Component child) {
248         Document JavaDoc document = parentNode.getOwnerDocument();
249         String JavaDoc childId = ContainerInstance.getElementId(child);
250         Element JavaDoc tdElement = document.createElement("td");
251         String JavaDoc cellId = ContainerInstance.getElementId(component) + "_cell_" + childId;
252         tdElement.setAttribute("id", cellId);
253         
254         // Configure cell style.
255
CssStyle cssStyle = new CssStyle();
256         RowLayoutData layoutData = getLayoutData(child);
257         CellLayoutDataRender.renderToElementAndStyle(tdElement, cssStyle, child, layoutData, "0px");
258         CellLayoutDataRender.renderBackgroundImageToStyle(cssStyle, rc, this, component, child);
259         if (layoutData != null) {
260             ExtentRender.renderToStyle(cssStyle, "width", layoutData.getWidth());
261         }
262         tdElement.setAttribute("style", cssStyle.renderInline());
263         
264         parentNode.appendChild(tdElement);
265         
266         renderSpacingCell(parentNode, (Row) component, child);
267         
268         renderAddChild(rc, update, tdElement, child);
269     }
270     
271     /**
272      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderDispose(nextapp.echo2.webcontainer.RenderContext,
273      * nextapp.echo2.app.update.ServerComponentUpdate, nextapp.echo2.app.Component)
274      */

275     public void renderDispose(RenderContext rc, ServerComponentUpdate update, Component component) { }
276
277     /**
278      * @see nextapp.echo2.webcontainer.DomUpdateSupport#renderHtml(nextapp.echo2.webcontainer.RenderContext,
279      * nextapp.echo2.app.update.ServerComponentUpdate, org.w3c.dom.Node, nextapp.echo2.app.Component)
280      */

281     public void renderHtml(RenderContext rc, ServerComponentUpdate update, Node JavaDoc parentNode, Component component) {
282         Row row = (Row) component;
283         Border border = (Border) row.getRenderProperty(Row.PROPERTY_BORDER);
284         String JavaDoc elementId = ContainerInstance.getElementId(row);
285         Document JavaDoc document = parentNode.getOwnerDocument();
286         
287         Element JavaDoc divElement = document.createElement("div");
288         divElement.setAttribute("id", elementId);
289         parentNode.appendChild(divElement);
290
291         CssStyle divCssStyle = new CssStyle();
292         BorderRender.renderToStyle(divCssStyle, border);
293         ColorRender.renderToStyle(divCssStyle, (Color) row.getRenderProperty(Row.PROPERTY_FOREGROUND),
294                 (Color) row.getRenderProperty(Row.PROPERTY_BACKGROUND));
295         FontRender.renderToStyle(divCssStyle, (Font) row.getRenderProperty(Row.PROPERTY_FONT));
296         Insets insets = (Insets) row.getRenderProperty(Row.PROPERTY_INSETS);
297         if (insets == null) {
298             divCssStyle.setAttribute("padding", "0px");
299         } else {
300             InsetsRender.renderToStyle(divCssStyle, "padding", insets);
301         }
302         divElement.setAttribute("style", divCssStyle.renderInline());
303         
304         Element JavaDoc tableElement = document.createElement("table");
305         tableElement.setAttribute("id", elementId + "_table");
306         tableElement.setAttribute("style", "padding:0px;border-collapse:collapse;");
307         
308         AlignmentRender.renderToElement(divElement,
309                 ((Alignment) row.getRenderProperty(Row.PROPERTY_ALIGNMENT)), row);
310         
311         divElement.appendChild(tableElement);
312         
313         Element JavaDoc tbodyElement = document.createElement("tbody");
314         tbodyElement.setAttribute("id", elementId + "_tbody");
315         tableElement.appendChild(tbodyElement);
316         
317         Element JavaDoc trElement = document.createElement("tr");
318         trElement.setAttribute("id", elementId + "_tr");
319         tbodyElement.appendChild(trElement);
320         
321         Component[] children = row.getVisibleComponents();
322         for (int i = 0; i < children.length; ++i) {
323             renderChild(rc, update, trElement, component, children[i]);
324         }
325         
326         storeRenderState(rc, row);
327     }
328     
329     /**
330      * Renders removal operations for child components which were removed from
331      * a <code>Row</code>, as described in the provided
332      * <code>ServerComponentUpdate</code>.
333      *
334      * @param rc the relevant <code>RenderContext</code>
335      * @param update the update
336      */

337     private void renderRemoveChildren(RenderContext rc, ServerComponentUpdate update) {
338         Component[] removedChildren = update.getRemovedChildren();
339         Component parent = update.getParent();
340         String JavaDoc parentId = ContainerInstance.getElementId(parent);
341         for (int i = 0; i < removedChildren.length; ++i) {
342             String JavaDoc childId = ContainerInstance.getElementId(removedChildren[i]);
343             DomUpdate.renderElementRemove(rc.getServerMessage(),
344                     parentId + "_cell_" + childId);
345             DomUpdate.renderElementRemove(rc.getServerMessage(),
346                     parentId + "_spacing_" + childId);
347         }
348
349         int componentCount = parent.getVisibleComponentCount();
350         if (componentCount > 0) {
351             DomUpdate.renderElementRemove(rc.getServerMessage(), parentId + "_spacing_"
352                     + ContainerInstance.getElementId(parent.getVisibleComponent(componentCount - 1)));
353         }
354     }
355     
356     /**
357      * Renders a "spacing cell" beneath a row cell to provide
358      * cell spacing.
359      *
360      * @param parentNode the containing node to which the child
361      * should be appended
362      * @param row the <code>Row</code> being updated
363      * @param child the child preceeding the spacing row
364      */

365     private void renderSpacingCell(Node JavaDoc parentNode, Row row, Component child) {
366         Extent cellSpacing = (Extent) row.getRenderProperty(Row.PROPERTY_CELL_SPACING);
367         if (!ExtentRender.isZeroLength(cellSpacing) && row.visibleIndexOf(child) != row.getVisibleComponentCount() - 1) {
368             Element JavaDoc spacingElement = parentNode.getOwnerDocument().createElement("td");
369             spacingElement.setAttribute("id", ContainerInstance.getElementId(row) + "_spacing_"
370                     + ContainerInstance.getElementId(child));
371             CssStyle spacingCssStyle = new CssStyle();
372             spacingCssStyle.setAttribute("width", ExtentRender.renderCssAttributeValue(cellSpacing));
373             spacingCssStyle.setAttribute("font-size", "1px");
374             spacingCssStyle.setAttribute("line-height", "0px");
375             spacingElement.setAttribute("style", spacingCssStyle.renderInline());
376             parentNode.appendChild(spacingElement);
377         }
378     }
379     
380     /**
381      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
382      * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
383      */

384     public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String JavaDoc targetId) {
385         // Determine if fully replacing the component is required.
386
boolean fullReplace = false;
387         if (update.hasUpdatedLayoutDataChildren()) {
388             // TODO: Perform fractional update on LayoutData change instead of full replace.
389
fullReplace = true;
390         } else if (update.hasUpdatedProperties()) {
391             if (!partialUpdateManager.canProcess(rc, update)) {
392                 fullReplace = true;
393             }
394         }
395         
396         if (fullReplace) {
397             // Perform full update.
398
DomUpdate.renderElementRemove(rc.getServerMessage(), ContainerInstance.getElementId(update.getParent()));
399             renderAdd(rc, update, targetId, update.getParent());
400         } else {
401             // Perform incremental updates.
402
if (update.hasRemovedChildren()) {
403                 renderRemoveChildren(rc, update);
404             }
405             if (update.hasUpdatedProperties()) {
406                 partialUpdateManager.process(rc, update);
407             }
408             if (update.hasAddedChildren()) {
409                 renderAddChildren(rc, update);
410             }
411         }
412         
413         storeRenderState(rc, update.getParent());
414         return fullReplace;
415     }
416
417     /**
418      * Update the stored <code>RenderState</code>.
419      *
420      * @param rc the relevant <code>RenderContext</code>
421      * @param component the <code>Row</code> component
422      */

423     private void storeRenderState(RenderContext rc, Component component) {
424         int componentCount = component.getVisibleComponentCount();
425         RowPeerRenderState renderState = new RowPeerRenderState();
426         if (componentCount > 0) {
427             renderState.lastChild = component.getVisibleComponent(componentCount - 1);
428         }
429         rc.getContainerInstance().setRenderState(component, renderState);
430     }
431 }
432
Popular Tags