KickJava   Java API By Example, From Geeks To Geeks.

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


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

74 public class ColumnPeer
75 implements ComponentSynchronizePeer, DomUpdateSupport, ImageRenderSupport {
76     
77     /**
78      * <code>RenderState</code> implementation.
79      */

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

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

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

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

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

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

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

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

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

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

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

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

278     public void renderHtml(RenderContext rc, ServerComponentUpdate update, Node JavaDoc parentNode, Component component) {
279         Column column = (Column) component;
280         
281         Document JavaDoc document = parentNode.getOwnerDocument();
282         Element JavaDoc divElement = document.createElement("div");
283         divElement.setAttribute("id", ContainerInstance.getElementId(column));
284         
285         CssStyle divCssStyle = new CssStyle();
286         BorderRender.renderToStyle(divCssStyle, (Border) column.getRenderProperty(Column.PROPERTY_BORDER));
287         ColorRender.renderToStyle(divCssStyle, (Color) column.getRenderProperty(Column.PROPERTY_FOREGROUND),
288                 (Color) column.getRenderProperty(Column.PROPERTY_BACKGROUND));
289         FontRender.renderToStyle(divCssStyle, (Font) column.getRenderProperty(Column.PROPERTY_FONT));
290         Insets insets = (Insets) column.getRenderProperty(Column.PROPERTY_INSETS);
291         if (insets == null) {
292             divCssStyle.setAttribute("padding", "0px");
293         } else {
294             InsetsRender.renderToStyle(divCssStyle, "padding", insets);
295         }
296         
297         divElement.setAttribute("style", divCssStyle.renderInline());
298         
299         parentNode.appendChild(divElement);
300         
301         Component[] children = column.getVisibleComponents();
302         for (int i = 0; i < children.length; ++i) {
303             renderChild(rc, update, divElement, component, children[i]);
304         }
305         
306         storeRenderState(rc, column);
307     }
308     
309     /**
310      * Renders removal operations for child components which were removed from
311      * a <code>Column</code>, as described in the provided
312      * <code>ServerComponentUpdate</code>.
313      *
314      * @param rc the relevant <code>RenderContext</code>
315      * @param update the update
316      */

317     private void renderRemoveChildren(RenderContext rc, ServerComponentUpdate update) {
318         Component[] removedChildren = update.getRemovedChildren();
319         Component parent = update.getParent();
320         String JavaDoc parentId = ContainerInstance.getElementId(parent);
321         for (int i = 0; i < removedChildren.length; ++i) {
322             String JavaDoc childId = ContainerInstance.getElementId(removedChildren[i]);
323             DomUpdate.renderElementRemove(rc.getServerMessage(),
324                     parentId + "_cell_" + childId);
325             DomUpdate.renderElementRemove(rc.getServerMessage(),
326                     parentId + "_spacing_" + childId);
327         }
328
329         int componentCount = parent.getVisibleComponentCount();
330         if (componentCount > 0) {
331             DomUpdate.renderElementRemove(rc.getServerMessage(), parentId + "_spacing_"
332                     + ContainerInstance.getElementId(parent.getVisibleComponent(componentCount - 1)));
333         }
334     }
335     
336     /**
337      * Renders a "spacing cell" beneath a column cell to provide
338      * cell spacing.
339      *
340      * @param parentNode the containing node to which the child
341      * should be appended
342      * @param column the <code>Column</code> being updated
343      * @param child the child preceeding the spacing column
344      */

345     private void renderSpacingCell(Node JavaDoc parentNode, Column column, Component child) {
346         Extent cellSpacing = (Extent) column.getRenderProperty(Column.PROPERTY_CELL_SPACING);
347         if (!ExtentRender.isZeroLength(cellSpacing) && column.visibleIndexOf(child) != column.getVisibleComponentCount() - 1) {
348             Element JavaDoc spacingElement = parentNode.getOwnerDocument().createElement("div");
349             spacingElement.setAttribute("id", ContainerInstance.getElementId(column) + "_spacing_"
350                     + ContainerInstance.getElementId(child));
351             CssStyle spacingCssStyle = new CssStyle();
352             spacingCssStyle.setAttribute("height", ExtentRender.renderCssAttributeValue(cellSpacing));
353             spacingCssStyle.setAttribute("font-size", "1px");
354             spacingCssStyle.setAttribute("line-height", "0px");
355             spacingElement.setAttribute("style", spacingCssStyle.renderInline());
356             parentNode.appendChild(spacingElement);
357         }
358     }
359     
360     /**
361      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
362      * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
363      */

364     public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String JavaDoc targetId) {
365         // Determine if fully replacing the component is required.
366
boolean fullReplace = false;
367         if (update.hasUpdatedLayoutDataChildren()) {
368             // TODO: Perform fractional update on LayoutData change instead of full replace.
369
fullReplace = true;
370         } else if (update.hasUpdatedProperties()) {
371             if (!partialUpdateManager.canProcess(rc, update)) {
372                 fullReplace = true;
373             }
374         }
375         
376         if (fullReplace) {
377             // Perform full update.
378
DomUpdate.renderElementRemove(rc.getServerMessage(), ContainerInstance.getElementId(update.getParent()));
379             renderAdd(rc, update, targetId, update.getParent());
380         } else {
381             // Perform incremental updates.
382
if (update.hasRemovedChildren()) {
383                 renderRemoveChildren(rc, update);
384             }
385             if (update.hasUpdatedProperties()) {
386                 partialUpdateManager.process(rc, update);
387             }
388             if (update.hasAddedChildren()) {
389                 renderAddChildren(rc, update);
390             }
391         }
392         
393         storeRenderState(rc, update.getParent());
394         return fullReplace;
395     }
396
397     /**
398      * Update the stored <code>RenderState</code>.
399      *
400      * @param rc the relevant <code>RenderContext</code>
401      * @param component the <code>Column</code> component
402      */

403     private void storeRenderState(RenderContext rc, Component component) {
404         int componentCount = component.getVisibleComponentCount();
405         ColumnPeerRenderState renderState = new ColumnPeerRenderState();
406         if (componentCount > 0) {
407             renderState.lastChild = component.getVisibleComponent(componentCount - 1);
408         }
409         rc.getContainerInstance().setRenderState(component, renderState);
410     }
411 }
412
Popular Tags