KickJava   Java API By Example, From Geeks To Geeks.

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


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 the
9  * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13  * the specific language governing rights and limitations under the License.
14  *
15  * Alternatively, the contents of this file may be used under the terms of
16  * either the GNU General Public License Version 2 or later (the "GPL"), or the
17  * GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
18  * case the provisions of the GPL or the LGPL are applicable instead of those
19  * above. If you wish to allow use of your version of this file only under the
20  * terms of either the GPL or the LGPL, and not to allow others to use your
21  * version of this file under the terms of the MPL, indicate your decision by
22  * deleting the provisions above and replace them with the notice and other
23  * provisions required by the GPL or the LGPL. If you do not delete the
24  * provisions above, a recipient may use your version of this file under the
25  * terms of any one of the MPL, the GPL or the LGPL.
26  */

27
28 package nextapp.echo2.webcontainer.syncpeer;
29
30 import java.util.ArrayList JavaDoc;
31 import java.util.BitSet JavaDoc;
32 import java.util.List JavaDoc;
33
34 import nextapp.echo2.app.Component;
35 import nextapp.echo2.app.Extent;
36 import nextapp.echo2.app.Grid;
37 import nextapp.echo2.app.LayoutData;
38 import nextapp.echo2.app.layout.GridLayoutData;
39
40 /**
41  * Provides analysis of a <code>Grid</code> for rendering purposes.
42  * <p>
43  * This object defines the <code>Grid</code> in terms of two axes, "x" and
44  * "y". The axes are transposed based on whether the <code>origin</code>
45  * property of the <code>Grid</code> is horizontal or vertical. For
46  * horizontally-oriented <code>Grid</code>s, the x-axis represents columns
47  * and the y-axis represents rows. For vertically oriented <code>Grid</code>s,
48  * the x-axis represents rows and the y-axis represents columns.
49  * <p>
50  * Once a <code>GridProcessor</code> has been instantiated, the rendering
51  * <code>GridPeer</code> can make inquiries to it to determine how the
52  * HTML table representing the Grid should be rendered.
53  * <p>
54  * Upon instantiation, the dimensions of the grid are calculated, and the
55  * content of each cell within those dimensions is determined. By specifying
56  * an "x" and "y" coordinate to various getXXX() methods, the renderer can
57  * determine what <code>Component</code> exists at a particular coordinate,
58  * how many rows and columns that <code>Component</code> spans, and the index
59  * of the <code>Component</code> within its parent <code>Grid</code>'s
60  * children.
61  * <p>
62  * This class should not be extended or used by classes outside of the Echo
63  * framework.
64  */

65 public class GridProcessor {
66     
67     private static final Integer JavaDoc DEFAULT_SIZE = new Integer JavaDoc(Grid.DEFAULT_SIZE);
68     
69     /**
70      * Internal representation of a rendered cell at a specific coordinate.
71      */

72     private class Cell {
73         
74         /**
75          * Creates a new <code>Cell</code>.
76          *
77          * @param component the represented <code>Component</code>
78          * @param index the index of <code>component</code> within the
79          * parent <code>Grid</code>
80          * @param xSpan the current calculated x-span of the cell
81          * @param ySpan the current calculated y-span of the cell
82          */

83         private Cell(Component component, int index, int xSpan, int ySpan) {
84             super();
85             this.component = component;
86             this.index = index;
87             this.xSpan = xSpan;
88             this.ySpan = ySpan;
89         }
90         
91         /** the current calculated x-span of the cell */
92         private int xSpan;
93
94         /** the current calculated y-span of the cell */
95         private int ySpan;
96
97         /** the represented <code>Component</code> */
98         private Component component;
99         
100         /**
101          * the index of <code>component</code> within the parent
102          * <code>Grid</code>
103          */

104         private int index;
105     }
106     
107     private Grid grid;
108     
109     /**
110      * A <code>List</code> containing arrays of <code>Cell</code>s.
111      * Each contained array of <code>Cell</code>s represents a single point on
112      * the y-axis. The cell indices represent points on the x-axis.
113      * Individual y-axis entries should be obtained via the
114      * <code>getCellArray()</code> method.
115      *
116      * @see #getCellArray(int, boolean)
117      */

118     private List JavaDoc cellArrays;
119
120     /** The current calculated size of the x-axis. */
121     private int gridXSize;
122     
123     /** The current calculated size of the y-axis. */
124     private int gridYSize;
125
126     /**
127      * The calculated dimensions of each array of cells on the x-axis.
128      * These values may change as a result of x-axis reductions.
129      */

130     private List JavaDoc xExtents;
131     
132     /**
133      * The calculated dimensions of each array of cells on the y-axis.
134      * These values may change as a result of y-axis reductions.
135      */

136     private List JavaDoc yExtents;
137     
138     /**
139      * Flag indicating whether grid has a horizontal (or vertical) orientation.
140      */

141     private boolean horizontalOrientation;
142     
143     /**
144      * Creates a new <code>GridProcessor</code> for the specified
145      * <code>Grid</code>. Creating a new <code>GridProcessor</code> will
146      * cause immediately analyze the <code>Grid</code> which will immediately
147      * consume processor and memory resources. Such operations should be done at
148      * most once per rendering.
149      *
150      * @param grid the <code>Grid</code>
151      */

152     public GridProcessor(Grid grid) {
153         super();
154         this.grid = grid;
155         cellArrays = new ArrayList JavaDoc();
156         
157         Integer JavaDoc orientationValue = (Integer JavaDoc) grid.getRenderProperty(Grid.PROPERTY_ORIENTATION);
158         int orientation = orientationValue == null ? Grid.ORIENTATION_HORIZONTAL : orientationValue.intValue();
159         horizontalOrientation = orientation != Grid.ORIENTATION_VERTICAL;
160         
161         Cell[] cells = createCells();
162         if (cells == null) {
163             // Special case: empty Grid.
164
gridXSize = 0;
165             gridYSize = 0;
166             return;
167         }
168         renderCellMatrix(cells);
169         
170         calculateExtents();
171         
172         reduceY();
173         reduceX();
174         trimX();
175     }
176     
177     private void calculateExtents() {
178         String JavaDoc xProperty = horizontalOrientation ? Grid.PROPERTY_COLUMN_WIDTH : Grid.PROPERTY_ROW_HEIGHT;
179         String JavaDoc yProperty = horizontalOrientation ? Grid.PROPERTY_ROW_HEIGHT : Grid.PROPERTY_COLUMN_WIDTH;
180         
181         xExtents = new ArrayList JavaDoc();
182         for (int i = 0; i < gridXSize; ++i) {
183             xExtents.add(grid.getRenderIndexedProperty(xProperty, i));
184         }
185         
186         yExtents = new ArrayList JavaDoc();
187         for (int i = 0; i < gridYSize; ++i) {
188             yExtents.add(grid.getRenderIndexedProperty(yProperty, i));
189         }
190     }
191     
192     /**
193      * Creates a one-dimensional array of <code>Cell</code> instances
194      * representing all visible components contained within the
195      * <code>Grid</code>.
196      *
197      * @return the array of <code>Cell</code>s
198      */

199     private Cell[] createCells() {
200         Component[] children = grid.getVisibleComponents();
201         
202         if (children.length == 0) {
203             // Abort if Grid is empty.
204
return null;
205         }
206
207         Cell[] cells = new Cell[children.length];
208
209         for (int i = 0; i < children.length; ++i) {
210             LayoutData layoutData = (LayoutData) children[i].getRenderProperty(Grid.PROPERTY_LAYOUT_DATA);
211             if (layoutData instanceof GridLayoutData) {
212                 GridLayoutData gcLayoutData = (GridLayoutData) layoutData;
213                 int xSpan = horizontalOrientation ? gcLayoutData.getColumnSpan() : gcLayoutData.getRowSpan();
214                 int ySpan = horizontalOrientation ? gcLayoutData.getRowSpan() : gcLayoutData.getColumnSpan();
215                 cells[i] = new Cell(children[i], i, xSpan, ySpan);
216             } else {
217                 cells[i] = new Cell(children[i], i, 1, 1);
218             }
219         }
220         return cells;
221     }
222     
223     /**
224      * Retrieves the array of cells representing a particular coordinate on
225      * the y-axis of the rendered representation.
226      *
227      * @param y the y-axis index
228      * @param expand a flag indicating whether the <code>CellArray</code>
229      * should be expanded in the event the given y-axis does not
230      * exist.
231      * @see #cellArrays
232      */

233     private Cell[] getCellArray(int y, boolean expand) {
234         while (expand && y >= cellArrays.size()) {
235             cellArrays.add(new Cell[gridXSize]);
236         }
237         return (Cell[]) cellArrays.get(y);
238     }
239     
240     /**
241      * Returns the <code>Component</code> that should be rendered at the
242      * specified position.
243      *
244      * @param column the column index
245      * @param row the row index
246      * @return the <code>Component</code> (may be null)
247      */

248     public Component getContent(int column, int row) {
249         if (horizontalOrientation) {
250             Cell cell = getCellArray(row, false)[column];
251             return cell == null ? null : cell.component;
252         } else {
253             Cell cell = getCellArray(column, false)[row];
254             return cell == null ? null : cell.component;
255         }
256     }
257
258     /**
259      * Returns the index of the <code>Component</code> that should be rendered
260      * at the specified position within its parent <code>Grid</code>
261      * container.
262      *
263      * @param column the column index
264      * @param row the row index
265      * @return the index of the <code>Component</code> within its
266      * container.
267      */

268     public int getComponentIndex(int column, int row) {
269         if (horizontalOrientation) {
270             Cell cell = getCellArray(row, false)[column];
271             return cell == null ? -1 : cell.index;
272         } else {
273             Cell cell = getCellArray(column, false)[row];
274             return cell == null ? -1 : cell.index;
275         }
276     }
277     
278     /**
279      * Returns the number of columns that should be rendered.
280      *
281      * @return the number of rendered columns
282      */

283     public int getColumnCount() {
284         return horizontalOrientation ? gridXSize : gridYSize;
285     }
286     
287     /**
288      * Returns the number of rows that should be rendered.
289      *
290      * @return the number of rendered rows
291      */

292     public int getRowCount() {
293         return horizontalOrientation ? gridYSize : gridXSize;
294     }
295     
296     /**
297      * Returns the width of the specified column index
298      *
299      * @param column the column index
300      * @return the width
301      */

302     public Extent getColumnWidth(int column) {
303         return (Extent) (horizontalOrientation ? xExtents : yExtents).get(column);
304     }
305     
306     /**
307      * Returns the height of the specified row index
308      *
309      * @param row the row index
310      * @return the height
311      */

312     public Extent getRowHeight(int row) {
313         return (Extent) (horizontalOrientation ? yExtents : xExtents).get(row);
314     }
315     
316     /**
317      * Returns the column span of the cell at the specified rendered index.
318      *
319      * @param column the column index
320      * @param row the row index
321      * @return the column span (-1 will be returned in the event that no
322      * cell exists at the specified index)
323      */

324     public int getColumnSpan(int column, int row) {
325         if (horizontalOrientation) {
326             Cell cell = getCellArray(row, false)[column];
327             return cell == null ? -1 : cell.xSpan;
328         } else {
329             Cell cell = getCellArray(column, false)[row];
330             return cell == null ? -1 : cell.ySpan;
331         }
332     }
333     
334     /**
335      * Returns the row span of the cell at the specified rendered index.
336      *
337      * @param column the column index
338      * @param row the row index
339      * @return the row span (-1 will be returned in the event that no
340      * cell exists at the specified index)
341      */

342     public int getRowSpan(int column, int row) {
343         if (horizontalOrientation) {
344             Cell cell = getCellArray(row, false)[column];
345             return cell == null ? -1 : cell.ySpan;
346         } else {
347             Cell cell = getCellArray(column, false)[row];
348             return cell == null ? -1 : cell.xSpan;
349         }
350     }
351
352     /**
353      * Remove duplicates from the x-axis where all cells simply
354      * "span over" a given x-axis coordinate.
355      */

356     private void reduceX() {
357         // Determine duplicate cell sets on x-axis.
358
BitSet JavaDoc xRemoves = new BitSet JavaDoc();
359         int x = 1;
360         int length = getCellArray(0, false).length;
361         while (x < length) {
362             int y = 0;
363             boolean identical = true;
364             while (y < cellArrays.size()) {
365                 if (getCellArray(y, false)[x] != getCellArray(y, false)[x - 1]) {
366                     identical = false;
367                     break;
368                 }
369                 ++y;
370             }
371             if (identical) {
372                 xRemoves.set(x, true);
373             }
374             ++x;
375         }
376
377         // If no reductions are necessary on the x-axis, do nothing.
378
if (xRemoves.nextSetBit(0) == -1) {
379             return;
380         }
381         
382         for (int removedX = gridXSize - 1; removedX >= 0; --removedX) {
383             if (!xRemoves.get(removedX)) {
384                 continue;
385             }
386
387             for (int y = 0; y < gridYSize; ++y) {
388                 if (y == 0 || getCellArray(y, false)[removedX - 1] != getCellArray(y - 1, false)[removedX - 1]) {
389                     // Reduce x-span, taking care not to reduce it multiple times if cell has a y-span.
390
Cell[] cellArray = getCellArray(y, false);
391                     if (cellArray[removedX - 1] != null) {
392                         --getCellArray(y, false)[removedX - 1].xSpan;
393                     }
394                 }
395                 for (x = removedX; x < gridXSize - 1; ++x) {
396                     getCellArray(y, false)[x] = getCellArray(y, false)[x + 1];
397                 }
398             }
399             
400             // Calculate Extent-size of merged indices.
401
Extent retainedExtent = (Extent) xExtents.get(removedX - 1);
402             Extent removedExtent = (Extent) xExtents.get(removedX);
403             xExtents.remove(removedX);
404             if (removedExtent != null) {
405                 xExtents.set(removedX - 1, Extent.add(removedExtent, retainedExtent));
406             }
407             
408             --gridXSize;
409         }
410     }
411     
412     /**
413      * Remove duplicates from the y-axis where all cells simply
414      * "span over" a given y-axis coordinate.
415      */

416     private void reduceY() {
417         // Determine duplicate cell sets on y-axis.
418
BitSet JavaDoc yRemoves = new BitSet JavaDoc();
419         int y = 1;
420         
421         int size = cellArrays.size();
422         Cell[] previousCellArray;
423         Cell[] currentCellArray = getCellArray(0, false);
424         
425         while (y < size) {
426             previousCellArray = currentCellArray;
427             currentCellArray = getCellArray(y, false);
428             
429             int x = 0;
430             boolean identical = true;
431
432             while (x < currentCellArray.length) {
433                 if (currentCellArray[x] != previousCellArray[x]) {
434                     identical = false;
435                     break;
436                 }
437                 ++x;
438             }
439             if (identical) {
440                 yRemoves.set(y, true);
441             }
442             
443             ++y;
444         }
445         
446         // If no reductions are necessary on the y-axis, do nothing.
447
if (yRemoves.nextSetBit(0) == -1) {
448             return;
449         }
450             
451         for (int removedY = gridYSize -1; removedY >= 0; --removedY) {
452             if (!yRemoves.get(removedY)) {
453                 continue;
454             }
455             
456             // Shorten the y-spans of the cell array that will be retained to
457
// reflect the fact that a cell array is being removed.
458
Cell[] retainedCellArray = getCellArray(removedY - 1, false);
459             for (int x = 0; x < gridXSize; ++x) {
460                 if (x == 0 || retainedCellArray[x] != retainedCellArray[x - 1]) {
461                     // Reduce y-span, taking care not to reduce it multiple times if cell has a x-span.
462
if (retainedCellArray[x] != null) {
463                         --retainedCellArray[x].ySpan;
464                     }
465                 }
466             }
467             
468             // Remove the duplicate cell array.
469
cellArrays.remove(removedY);
470             
471             // Calculate Extent-size of merged indices.
472
Extent retainedExtent = (Extent) yExtents.get(removedY - 1);
473             Extent removedExtent = (Extent) yExtents.get(removedY);
474             yExtents.remove(removedY);
475             if (removedExtent != null) {
476                 yExtents.set(removedY - 1, Extent.add(removedExtent, retainedExtent));
477             }
478
479             // Decrement the grid size to reflect cell array removal.
480
--gridYSize;
481         }
482     }
483     
484     private void renderCellMatrix(Cell[] cells) {
485         gridXSize = ((Integer JavaDoc) grid.getRenderProperty(Grid.PROPERTY_SIZE, DEFAULT_SIZE)).intValue();
486         
487         int x = 0, y = 0;
488         Cell[] yCells = getCellArray(y, true);
489         for (int componentIndex = 0; componentIndex < cells.length; ++componentIndex) {
490
491             // Set x-span to fill remaining size in the even SPAN_FILL has been specified or if the cell would
492
// otherwise extend past the specified size.
493
if (cells[componentIndex].xSpan == GridLayoutData.SPAN_FILL || cells[componentIndex].xSpan > gridXSize - x) {
494                 cells[componentIndex].xSpan = gridXSize - x;
495             }
496
497             // Set x-span of any cell INCORRECTLY set to negative value to 1 (note that SPAN_FILL has already been handled).
498
if (cells[componentIndex].xSpan < 1) {
499                 cells[componentIndex].xSpan = 1;
500             }
501             // Set y-span of any cell INCORRECTLY set to negative value (or more likely SPAN_FILL) to 1.
502
if (cells[componentIndex].ySpan < 1) {
503                 cells[componentIndex].ySpan = 1;
504             }
505             
506             if (cells[componentIndex].xSpan != 1 || cells[componentIndex].ySpan != 1) {
507                 // Scan to ensure no y-spans are blocking this x-span.
508
// If a y-span is blocking, shorten the x-span to not
509
// interfere.
510
for (int xIndex = 1; xIndex < cells[componentIndex].xSpan; ++xIndex) {
511                     if (yCells[x + xIndex] != null) {
512                         // Blocking component found.
513
cells[componentIndex].xSpan = xIndex;
514                         break;
515                     }
516                 }
517                 for (int yIndex = 0; yIndex < cells[componentIndex].ySpan; ++yIndex) {
518                     Cell[] yIndexCells = getCellArray(y + yIndex, true);
519                     for (int xIndex = 0; xIndex < cells[componentIndex].xSpan; ++xIndex) {
520                         yIndexCells[x + xIndex] = cells[componentIndex];
521                     }
522                 }
523             }
524             yCells[x] = cells[componentIndex];
525
526             if (componentIndex < cells.length - 1) {
527                 // Move rendering cursor.
528
boolean nextRenderPointFound = false;
529                 while (!nextRenderPointFound) {
530                     if (x < gridXSize - 1) {
531                         ++x;
532                     } else {
533                         // Move cursor to next line.
534
x = 0;
535                         ++y;
536                         yCells = getCellArray(y, true);
537                         
538                     }
539                     nextRenderPointFound = yCells[x] == null;
540                 }
541             }
542         }
543
544         // Store actual 'y' dimension.
545
gridYSize = cellArrays.size();
546     }
547     
548     /**
549      * Special case: Trim excess null cells from Grid x-size if the
550      * Grid y-size is 1.
551      */

552     private void trimX() {
553         if (gridYSize == 1) {
554             Cell[] cellArray = getCellArray(0, false);
555             for (int i = 0; i < gridXSize; ++i) {
556                 if (cellArray[i] == null) {
557                     gridXSize = i;
558                 }
559             }
560         }
561     }
562 }
563
Popular Tags