1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.portlets.layout;
18
19 import java.io.Serializable;
20 import java.text.DecimalFormat;
21 import java.text.DecimalFormatSymbols;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.SortedMap;
31 import java.util.TreeMap;
32
33 import org.apache.jetspeed.om.page.Fragment;
34
35 /***
36 * <h2>Basics</h2>
37 * <p>
38 * <code>ColumnLayout</code> is the model used to support any 1 to <i>n</i>
39 * column-based layout. <code>ColumnLayout</code> is constrained by a number
40 * columns that will not be exceeded, even if a fragment specifies a column
41 * outside of this constraint. Any fragment exceeded the specified column
42 * constraint will be deposited into the right-most column.
43 * </p>
44 *
45 * <h2>Characteristics:</h2>
46 * <ul>
47 * <li>Columns and rows always start at 0.</li>
48 * <li>Unless otherwise noted, assume all Collections returned are immutable.</li>
49 * <li>Unless otherwise noted, assume that no public method will ever return <code>null</code>.</li>
50 * </ul>
51 *
52 *
53 * <h2>Layout Events</h2>
54 * <p>
55 * When any move*() method is invoked and a portlet is actually moved (see indvidual
56 * methods for what causes these circumstances), an initial LayoutEvent is dispatched.
57 * This may cause a cascade of LayoutEvents to be fired in turn if the movement of the
58 * target fragment cause other fragments to be repositioned. In this case a LayoutEvent
59 * is dispatched for each portlet moved, which in turn may our may not cause another
60 * LayoutEvent to be fired.
61 * </p>
62 * @see org.apache.jetspeed.portlets.layout.LayoutEvent
63 * @see org.apache.jetspeed.portlets.layout.LayoutEventListener
64 * @see org.apache.jetspeed.portlets.layout.LayoutCoordinate
65 * @see org.apache.jetspeed.om.page.Fragment
66 *
67 * @author <href a="mailto:weaver@apache.org">Scott T. Weaver</a>
68 *
69 */
70 public class ColumnLayout implements Serializable
71 {
72 /*** Percentage widths gutter width */
73 private final static double PERCENTAGE_WIDTH_GUTTER = 0.01;
74
75 /*** Percentage widths format */
76 private final static DecimalFormat PERCENTAGE_WIDTH_FORMAT = new DecimalFormat("0.00'%'", new DecimalFormatSymbols(
77 Locale.ENGLISH));
78
79 /*** Constrains the columns for this layout */
80 private final int numberOfColumns;
81
82 /*** SortedMap of Columns (which are also sorted maps */
83 private final SortedMap columns;
84
85 /*** Width settings for eacah column */
86 private final String[] columnWidths;
87
88 /*** Efficent way to always be aware of the next available row in a column */
89 private final int[] nextRowNumber;
90
91 /*** maps Fragments (key) to it's current LayoutCoordinate (value) in this layout */
92 private final Map coordinates;
93
94 /*** All of the LayoutEventListeners registered to this layout */
95 private final List eventListeners;
96
97 /***
98 *
99 * @param numberOfColumns
100 * the maximum number of columns this layout will have.
101 * @param layoutType
102 * this value corresponds to the property settings of the
103 * fragments within your psml. Layout type allows segration of
104 * property settings based on the type of layout in use. This
105 * effectively allows for the interchange of multiple layout
106 * formats without one format effecting the settings of another.
107 * @param columnWidths
108 * widths for each column that accumulate to 100% if percentages
109 * are used.
110 * @see org.apache.jetspeed.om.page.Fragment#getType()
111 */
112 public ColumnLayout(int numberOfColumns, String layoutType, String[] columnWidths)
113 {
114 this.numberOfColumns = numberOfColumns;
115 this.columnWidths = columnWidths;
116 eventListeners = new ArrayList();
117
118 columns = new TreeMap();
119 coordinates = new HashMap();
120
121 for (int i = 0; i < numberOfColumns; i++)
122 {
123 columns.put(new Integer(i), new TreeMap());
124 }
125
126 nextRowNumber = new int[numberOfColumns];
127
128 for (int i = 0; i < numberOfColumns; i++)
129 {
130 nextRowNumber[i] = 0;
131 }
132 }
133
134 /***
135 * Same as ColumnLayout(int numberOfColumns, String layoutType) but also
136 * supplies a Collection of fragmetns to initially populate the layout
137 * with. Adding these fragments <strong>WILL NOT</strong> cause
138 * a LayoutEvent to be dispatched.
139 *
140 * @see ColumnLayout(int numberOfColumns, String layoutType)
141 * @param numberOfColumns
142 * the maximum number of columns this layout will have.
143 * @param layoutType
144 * this value corresponds to the property settings of the
145 * fragments within your psml. Layout type allows segration of
146 * property settings based on the type of layout in use. This
147 * effectively allows for the interchange of multiple layout
148 * formats without one format effecting the settings of another.
149 * @param fragments Initial set of fragments to add to this layout.
150 * @param columnWidths
151 * widths for each column that accumulate to 100% if percentages
152 * are used.
153 * @throws LayoutEventException
154 */
155 public ColumnLayout(int numberOfColumns, String layoutType, Collection fragments, String[] columnWidths) throws LayoutEventException
156 {
157 this(numberOfColumns, layoutType, columnWidths);
158 Iterator fragmentsItr = fragments.iterator();
159 try
160 {
161 while (fragmentsItr.hasNext())
162 {
163 Fragment fragment = (Fragment) fragmentsItr.next();
164 doAdd(getColumn(fragment), getRow(getColumn(fragment), fragment), fragment);
165 }
166 }
167 catch (InvalidLayoutLocationException e)
168 {
169
170
171
172 throw new LayoutError("A malformed fragment could not be adjusted.", e);
173 }
174 }
175
176 /***
177 * <p>
178 * Adds a fragment to the layout using fragment properties of
179 * <code> row </code> and <code> column </code> as hints on where to put
180 * this fragment. The following rules apply to malformed fragment
181 * definitions:
182 * </p>
183 * <ul>
184 * <li>Fragments without a row defined are placed at the bottom of their
185 * respective column </li>
186 * <li>Fragments without a column are placed in the right-most column.
187 * </li>
188 * <li> Fragments with overlapping row numbers. The last fragment has
189 * priority pushing the fragment in that row down one row. </li>
190 * </ul>
191 *
192 * @param fragment
193 * Fragment to add to this layout.
194 * @throws LayoutEventException
195 * @see org.apache.jetspeed.om.page.Fragment
196 *
197 */
198 public void addFragment(Fragment fragment) throws LayoutEventException
199 {
200 try
201 {
202 doAdd(getColumn(fragment), getRow(getColumn(fragment), fragment), fragment);
203 LayoutCoordinate coordinate = getCoordinate(fragment);
204 processEvent(new LayoutEvent(LayoutEvent.ADDED, fragment, coordinate, coordinate));
205 }
206 catch (InvalidLayoutLocationException e)
207 {
208
209
210
211 throw new LayoutError("A malformed fragment could not be adjusted.", e);
212 }
213 catch (FragmentNotInLayoutException e)
214 {
215 throw new LayoutError("Failed to add coordinate to this ColumnLayout.", e);
216 }
217 }
218
219 /***
220 * Adds a LayoutEventListener to this layout that will be fired any time
221 * a LayoutEvent is disaptched.
222 *
223 * @param eventListener
224 * @see LayoutEventListener
225 * @see LayoutEventListener
226 */
227 public void addLayoutEventListener(LayoutEventListener eventListener)
228 {
229 eventListeners.add(eventListener);
230 }
231
232 /***
233 *
234 * @param columnNumber
235 * Number of column to retreive
236 * @return requested column (as a immutable Collection). Never returns
237 * <code>null.</code>
238 * @throws InvalidLayoutLocationException
239 * if the column is outisde of the constraints of this layout
240 */
241 public Collection getColumn(int columnNumber) throws InvalidLayoutLocationException
242 {
243 return Collections.unmodifiableCollection(getColumnMap(columnNumber).values());
244 }
245
246 /***
247 * returns the width to be used with the specified column. If
248 * there is no specific column setting sfor the specified column
249 * 0 is returned.
250 *
251 * @param columnNumber whose width has been requested.
252 * @return the width to be used with the specified column. Or 0 if no value
253 * has been specified.
254 */
255 public String getColumnWidth(int columnNumber)
256 {
257 if ((columnWidths != null) && (columnNumber < numberOfColumns))
258 {
259 String columnWidth = columnWidths[columnNumber];
260
261
262
263
264 if ((numberOfColumns > 1) && (columnNumber == (numberOfColumns - 1)))
265 {
266 int percentIndex = columnWidth.lastIndexOf('%');
267 if (percentIndex > 0)
268 {
269 try
270 {
271 double width = Double.parseDouble(columnWidth.substring(0,percentIndex).trim());
272 synchronized (PERCENTAGE_WIDTH_FORMAT)
273 {
274 columnWidth = PERCENTAGE_WIDTH_FORMAT.format(width - PERCENTAGE_WIDTH_GUTTER);
275 }
276 }
277 catch (NumberFormatException nfe)
278 {
279 }
280 }
281 }
282 return columnWidth;
283 }
284 return "0";
285 }
286
287 /***
288 * returns the float to be used with the specified column.
289 *
290 * @param columnNumber whose width has been requested.
291 * @return "right" for the last column, "left" if more than one
292 * column, or "none" otherwise.
293 */
294 public String getColumnFloat(int columnNumber)
295 {
296 if ((numberOfColumns > 1) && (columnNumber < numberOfColumns))
297 {
298 if (columnNumber == (numberOfColumns - 1))
299 {
300 return "right";
301 }
302 else
303 {
304 return "left";
305 }
306 }
307 return "none";
308 }
309
310 /***
311 * @return <code>java.util.Collection</code> all of columns (also
312 * Collection objects) in order within this layout. All Collections
313 * are immutable.
314 */
315 public Collection getColumns()
316 {
317 ArrayList columnList = new ArrayList(getNumberOfColumns());
318 Iterator itr = columns.values().iterator();
319 while (itr.hasNext())
320 {
321 columnList.add(Collections.unmodifiableCollection(((Map) itr.next()).values()));
322 }
323
324 return Collections.unmodifiableCollection(columnList);
325 }
326
327 /***
328 *
329 * Returns the index of the last row in the specified column.
330 *
331 * @param columnNumber column form whom we ant to identify the
332 * last row.
333 * @return the index of the last row in the specified column.
334 */
335 public int getLastRowNumber(int columnNumber)
336 {
337 return nextRowNumber[columnNumber] - 1;
338 }
339
340 /***
341 * Returns an immutable Collection of all the Fragments contained within
342 * this ColumnLayout in no sepcific order.
343 * @return Immutable Collection of Fragments.
344 */
345 public Collection getFragments()
346 {
347 return Collections.unmodifiableCollection(coordinates.keySet());
348 }
349
350 /***
351 * Retrieves the fragment at the specified loaction.
352 *
353 * @param columnNumber Column coordinate (first column starts at 0)
354 * @param rowNumber Row coordinate (first row starts at 0)
355 * @return Fragment at the specified coordinate. Never returns <code>null</code>.
356 * @throws EmptyLayoutLocationException if there is no fragment currently located at the specified coordinate.
357 * @throws InvalidLayoutLocationException if the coordinate lies outside the confines of this layout, i.e., the
358 * <code>columnNumber</code> exceeds the max columns setting for this layout.
359 */
360 public Fragment getFragmentAt(int columnNumber, int rowNumber) throws EmptyLayoutLocationException,
361 InvalidLayoutLocationException
362 {
363 SortedMap column = getColumnMap(columnNumber);
364 Integer rowInteger = new Integer(rowNumber);
365 if (column.containsKey(rowInteger))
366 {
367 return (Fragment) column.get(rowInteger);
368 }
369 else
370 {
371 throw new EmptyLayoutLocationException(columnNumber, rowNumber);
372 }
373 }
374
375 /***
376 *
377 * Retrieves the fragment at the specified loaction.
378 *
379 * @param coodinate LayoutCoordinate object that will be used to located a fragment in this
380 * layout.
381 * @see LayoutCoordinate
382 * @return Fragment at the specified coordinate. Never returns <code>null</code>.
383 * @throws EmptyLayoutLocationException if there is no fragment currently located at the specified coordinate.
384 * @throws InvalidLayoutLocationException if the coordinate lies outside the confines of this layout, i.e., the
385 * <code>columnNumber</code> exceeds the max columns setting for this layout.
386 * @see LayoutCoordinate
387 */
388 public Fragment getFragmentAt(LayoutCoordinate coodinate) throws EmptyLayoutLocationException,
389 InvalidLayoutLocationException
390 {
391 return getFragmentAt(coodinate.getX(), coodinate.getY());
392 }
393
394 /***
395 *
396 * @return The total number of columns in this layout.
397 */
398 public int getNumberOfColumns()
399 {
400 return numberOfColumns;
401 }
402
403 /***
404 *
405 * @return The last column in this layout. The Collection is immutable.
406 */
407 public Collection getLastColumn()
408 {
409 try
410 {
411 return Collections.unmodifiableCollection(getColumnMap(numberOfColumns - 1).values());
412 }
413 catch (InvalidLayoutLocationException e)
414 {
415
416
417 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify its last column.", e);
418 }
419 }
420
421 /***
422 *
423 * @return The last column in this layout. The Collection is immutable.
424 */
425 public Collection getFirstColumn()
426 {
427 try
428 {
429 return Collections.unmodifiableCollection(getColumnMap(0).values());
430 }
431 catch (InvalidLayoutLocationException e)
432 {
433
434
435 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify its first column.", e);
436 }
437 }
438
439 /***
440 *
441 * Moves a fragment one column to the right. A LayoutEvent is triggered by
442 * this action.
443 *
444 * <p>
445 * If the fragment currently
446 * resides in right-most column, no action is taking and no event LayoutEvent
447 * is fired.
448 * </p>
449 *
450 * @param fragment fragment to move.
451 * @throws FragmentNotInLayoutException if the specified fragment is not currently in the layout.
452 * @throws LayoutEventException If a triggered LayoutEvent fails.
453 */
454 public void moveRight(Fragment fragment) throws FragmentNotInLayoutException, LayoutEventException
455 {
456 LayoutCoordinate coordinate = getCoordinate(fragment);
457 LayoutCoordinate newCoordinate = new LayoutCoordinate(coordinate.getX() + 1, coordinate.getY());
458
459 if (newCoordinate.getX() < numberOfColumns)
460 {
461
462 try
463 {
464 doMove(fragment, coordinate, newCoordinate);
465 processEvent(new LayoutEvent(LayoutEvent.MOVED_RIGHT, fragment, coordinate, newCoordinate));
466
467 try
468 {
469 Fragment fragmentBelow = getFragmentAt(new LayoutCoordinate(coordinate.getX(), coordinate.getY() + 1));
470 moveUp(fragmentBelow);
471 }
472 catch (EmptyLayoutLocationException e)
473 {
474
475 }
476 }
477 catch (InvalidLayoutLocationException e)
478 {
479
480 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify valid column locations.", e);
481 }
482 }
483 }
484
485 /***
486 * Moves a fragment one column to the left. A LayoutEvent is triggered by
487 * this action.
488 *
489 * <p>
490 * If the fragment currently
491 * resides in left-most column, no action is taking and no event LayoutEvent
492 * is fired.
493 * </p>
494 *
495 * @param fragment
496 * @throws FragmentNotInLayoutException if the specified fragment is not currently in the layout.
497 * @throws LayoutEventException If a triggered LayoutEvent fails.
498 */
499 public void moveLeft(Fragment fragment) throws FragmentNotInLayoutException, LayoutEventException
500 {
501 LayoutCoordinate coordinate = getCoordinate(fragment);
502 LayoutCoordinate newCoordinate = new LayoutCoordinate(coordinate.getX() - 1, coordinate.getY());
503
504 if (newCoordinate.getX() >= 0)
505 {
506 try
507 {
508 doMove(fragment, coordinate, newCoordinate);
509 processEvent(new LayoutEvent(LayoutEvent.MOVED_LEFT, fragment, coordinate, newCoordinate));
510
511 try
512 {
513 Fragment fragmentBelow = getFragmentAt(new LayoutCoordinate(coordinate.getX(), coordinate.getY() + 1));
514 moveUp(fragmentBelow);
515 }
516 catch (EmptyLayoutLocationException e)
517 {
518
519 }
520 }
521 catch (InvalidLayoutLocationException e)
522 {
523
524 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify valid column locations.", e);
525 }
526
527 }
528
529 }
530
531 /***
532 * Moves a fragment one row to the up. A LayoutEvent is triggered by
533 * this action.
534 *
535 * <p>
536 * If the fragment currently
537 * resides in top-most row, no action is taking and no event LayoutEvent
538 * is fired.
539 * </p>
540 * @param fragment
541 * @throws FragmentNotInLayoutException if the specified fragment is not currently in the layout.
542 * @throws LayoutEventException If a triggered LayoutEvent fails.
543 */
544 public void moveUp(Fragment fragment) throws FragmentNotInLayoutException, LayoutEventException
545 {
546 LayoutCoordinate coordinate = getCoordinate(fragment);
547 LayoutCoordinate aboveLayoutCoordinate = new LayoutCoordinate(coordinate.getX(), coordinate.getY() - 1);
548 LayoutCoordinate newCoordinate = aboveLayoutCoordinate;
549
550
551 if (newCoordinate.getY() >= 0)
552 {
553 try
554 {
555 try
556 {
557
558
559 doMove(fragment, coordinate, newCoordinate);
560 processEvent(new LayoutEvent(LayoutEvent.MOVED_UP, fragment, coordinate, newCoordinate));
561 }
562 catch (EmptyLayoutLocationException e)
563 {
564
565 doMove(fragment, coordinate, newCoordinate);
566 processEvent(new LayoutEvent(LayoutEvent.MOVED_UP, fragment, coordinate, newCoordinate));
567
568
569 if(coordinate.getY() == (nextRowNumber[coordinate.getX()] - 1))
570 {
571 nextRowNumber[coordinate.getX()] = coordinate.getX();
572 }
573
574 try
575 {
576 Fragment fragmentBelow = getFragmentAt(new LayoutCoordinate(coordinate.getX(),
577 coordinate.getY() + 1));
578 moveUp(fragmentBelow);
579 }
580 catch (EmptyLayoutLocationException e1)
581 {
582
583 }
584 }
585 }
586 catch (InvalidLayoutLocationException e)
587 {
588
589 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify valid column locations.", e);
590 }
591 }
592 }
593
594 /***
595 *
596 * @param fragment
597 * @throws FragmentNotInLayoutException if the specified fragment is not currently in the layout.
598 * @throws LayoutEventException If a triggered LayoutEvent fails.
599 */
600 public void moveDown(Fragment fragment) throws FragmentNotInLayoutException, LayoutEventException
601 {
602 LayoutCoordinate coordinate = getCoordinate(fragment);
603 LayoutCoordinate newCoordinate = new LayoutCoordinate(coordinate.getX(), coordinate.getY() + 1);
604
605
606 if (newCoordinate.getY() < nextRowNumber[coordinate.getX()])
607 {
608 try
609 {
610 try
611 {
612
613
614 LayoutCoordinate aboveCoord = new LayoutCoordinate(coordinate.getX(), coordinate.getY() + 1);
615 Fragment fragmentBelow = getFragmentAt(aboveCoord);
616 doMove(fragmentBelow, aboveCoord, coordinate);
617 processEvent(new LayoutEvent(LayoutEvent.MOVED_UP, fragmentBelow, aboveCoord, coordinate));
618
619
620 }
621 catch (EmptyLayoutLocationException e)
622 {
623 doMove(fragment, coordinate, newCoordinate);
624 processEvent(new LayoutEvent(LayoutEvent.MOVED_DOWN, fragment, coordinate, newCoordinate));
625 }
626 }
627 catch (InvalidLayoutLocationException e)
628 {
629
630 throw new LayoutError("It appears this layout is corrupt and cannot correctly identify valid column locations.", e);
631 }
632
633 }
634 }
635
636 /***
637 * Performs the actual movement of a fragment.
638 *
639 *
640 * @param fragment
641 * @param oldCoordinate
642 * @param newCoordinate
643 * @throws InvalidLayoutLocationException
644 * @throws LayoutEventException
645 */
646 protected void doMove(Fragment fragment, LayoutCoordinate oldCoordinate, LayoutCoordinate newCoordinate)
647 throws InvalidLayoutLocationException, LayoutEventException
648 {
649 SortedMap oldColumn = getColumnMap(oldCoordinate.getX());
650 oldColumn.remove(new Integer(oldCoordinate.getY()));
651 coordinates.remove(fragment);
652
653 doAdd(newCoordinate.getX(), newCoordinate.getY(), fragment);
654 }
655
656 /***
657 *
658 *
659 * @param fragment fragment whose LayoutCoordinate we ant.
660 * @return LayoutCoordinate representing the current location of this
661 * Fragment within this layout.
662 * @throws FragmentNotInLayoutException if the Fragment is not present in this layout.
663 * @see LayoutCoordinate
664 */
665 public LayoutCoordinate getCoordinate(Fragment fragment) throws FragmentNotInLayoutException
666 {
667 if (coordinates.containsKey(fragment))
668 {
669 return (LayoutCoordinate) coordinates.get(fragment);
670 }
671 else
672 {
673 throw new FragmentNotInLayoutException(fragment);
674 }
675 }
676
677 /***
678 * Adds a fragment at the indicated <code>columnNumber</code>
679 * and <code>rowNumber</code>.
680 *
681 * @param columnNumber
682 * @param rowNumber
683 * @param fragment
684 * @throws InvalidLayoutLocationException if the coordinates are outside the bounds of this layout.
685 * @throws LayoutEventException id a LayoutEvent fails
686 */
687 protected void doAdd(int columnNumber, int rowNumber, Fragment fragment) throws InvalidLayoutLocationException, LayoutEventException
688 {
689 SortedMap column = getColumnMap(columnNumber);
690
691 Integer rowInteger = new Integer(rowNumber);
692 LayoutCoordinate targetCoordinate = new LayoutCoordinate(columnNumber, rowNumber);
693 if (column.containsKey(rowInteger))
694 {
695
696 Fragment existingFragment = (Fragment) column.get(rowInteger);
697 column.put(rowInteger, fragment);
698 coordinates.put(fragment, targetCoordinate);
699 doAdd(columnNumber, ++rowNumber, existingFragment);
700
701 LayoutCoordinate oneDownCoordinate = new LayoutCoordinate(targetCoordinate.getX(), targetCoordinate.getY() + 1);
702 processEvent(new LayoutEvent(LayoutEvent.MOVED_DOWN, existingFragment, targetCoordinate, oneDownCoordinate));
703 }
704 else
705 {
706 column.put(rowInteger, fragment);
707 coordinates.put(fragment, targetCoordinate);
708 rowNumber++;
709 if(rowNumber > nextRowNumber[columnNumber])
710 {
711 nextRowNumber[columnNumber] = rowNumber;
712 }
713 }
714
715 }
716
717 /***
718 * Retrieves this specified <code>columnNumber</code> as a
719 * SortedMap.
720 *
721 * @param columnNumber
722 * @return
723 * @throws InvalidLayoutLocationException if the <code>columnNumber</code> resides
724 * outside the bounds of this layout.
725 */
726 protected final SortedMap getColumnMap(int columnNumber) throws InvalidLayoutLocationException
727 {
728 Integer columnNumberIneteger = new Integer(columnNumber);
729
730 if (columns.containsKey(columnNumberIneteger))
731 {
732 return ((SortedMap) columns.get(columnNumberIneteger));
733 }
734 else
735 {
736 throw new InvalidLayoutLocationException(columnNumber);
737 }
738
739 }
740
741 /***
742 * Gets the row number of this fragment to looking the <code>layoutType</code>
743 * property <i>row</i>. If this property is undefined, the bottom-most row
744 * number of <code>currentColumn</code> is returned.
745 *
746 * @param currentColumn
747 * @param fragment
748 * @return valid row for this fragment within this layout.
749 */
750 protected final int getRow(int currentColumn, Fragment fragment)
751 {
752 String propertyValue = fragment.getProperty(Fragment.ROW_PROPERTY_NAME);
753 if (propertyValue != null)
754 {
755 return Integer.parseInt(propertyValue);
756 }
757 else
758 {
759 return nextRowNumber[currentColumn];
760 }
761
762 }
763
764 /***
765 * Gets the row number of this fragment to looking the <code>layoutType</code>
766 * property <i>column</i>.
767 *
768 * If the <i>column</i> is undefined or exceeds the constriants of this
769 * layout, the value returned is <code>numberOfColumns - 1</code>. If the
770 * value is less than 0, 0 is returned.
771 *
772 *
773 * @param fragment
774 * @return
775 */
776 protected final int getColumn(Fragment fragment)
777 {
778 String propertyValue = fragment.getProperty(Fragment.COLUMN_PROPERTY_NAME);
779 if (propertyValue != null)
780 {
781 int columnNumber = Integer.parseInt(propertyValue);
782
783
784 if (columnNumber >= numberOfColumns)
785 {
786 columnNumber = (numberOfColumns - 1);
787 }
788
789 else if (columnNumber < 0)
790 {
791 columnNumber = 0;
792 }
793
794 return columnNumber;
795 }
796 else
797 {
798 return (numberOfColumns - 1);
799 }
800 }
801
802 /***
803 * Dispatches a LayoutEvent to all LayoutEventListeners registered to this layout.
804 *
805 * @param event
806 * @throws LayoutEventException if an error occurs while processing a the LayoutEvent.
807 */
808 protected final void processEvent(LayoutEvent event) throws LayoutEventException
809 {
810 Iterator itr = eventListeners.iterator();
811 while(itr.hasNext())
812 {
813 LayoutEventListener eventListener = (LayoutEventListener) itr.next();
814 eventListener.handleEvent(event);
815 }
816
817 }
818
819 }