View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.layout;
17  
18  import java.awt.Dimension;
19  import java.awt.Rectangle;
20  import java.io.Serializable;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.StringTokenizer;
27  
28  /**
29   * <p>
30   * The main class of the percent layout manager.
31   * </p>
32   * <p>
33   * Percent layout provides a table-like layout, which is organized in columns
34   * and rows, each of which defined by a {@link CellConstraints}
35   * object. With these constraints objects it is possible to set a cell's
36   * alignment and its minimum size. A cell can also be assigned weight factors
37   * for its height and width. If there is more space available than needed by the
38   * existing cells, the remaining space is divided and assigned to cells with a
39   * weight factor greater than 0. So each cell can be given a certain percentage
40   * of the remaining space, thus the name of this layout manager.
41   * </p>
42   * <p>
43   * Sometimes certain columns or rows in the layout should have the same size,
44   * even if they contain components with different preferred or minimum sizes. To
45   * achieve this, {@link CellGroup} objects can be added to this
46   * layout manager. These objects define the indices of the columns and rows,
47   * which belong to the same group. All cells in a group have the same initial
48   * size. To ensure that the affected cells have always the same size, their
49   * weight factors must also be equal.
50   * </p>
51   * <p>
52   * This class is an abstract base class that implements the complete layouting
53   * algorithm. There will be concrete implementations for different layout types
54   * that are based on the central percent layout functionality. These classes
55   * will serve as adapters for specific layouts; they will create a layout
56   * description that can be handled by this base class.
57   * </p>
58   * <p>
59   * The family of percent layout classes is independent on a concrete GUI
60   * library. It can work together e.g. with Swing or SWT. To achieve this access
61   * to the managed GUI components is encapsulated by the
62   * {@link net.sf.jguiraffe.gui.layout.PercentLayoutPlatformAdapter}
63   * interface. A platform specific implementation of this interface must be
64   * passed to an instance of this class.
65   * </p>
66   * <p>
67   * Note: The {@code PercentLayout} class is not thread safe. It should be
68   * accessed by a single thread (the GUI thread) only.
69   * </p>
70   *
71   * @author Oliver Heger
72   * @version $Id: PercentLayoutBase.java 205 2012-01-29 18:29:57Z oheger $
73   */
74  public abstract class PercentLayoutBase implements Serializable
75  {
76      /**
77       * The serial version UID.
78       */
79      private static final long serialVersionUID = 20090730L;
80  
81      /** Constant for the delimiters for cell constraints. */
82      private static final String CONSTRAINTS_DELIMITERS = " ,;";
83  
84      /** Stores the column constraints. */
85      private CellConstraints[] columnConstraints;
86  
87      /** Stores the row constraints. */
88      private CellConstraints[] rowConstraints;
89  
90      /** Stores information about the contained components. */
91      private CellData[][] cells;
92  
93      /** The builder for creating constraints objects. */
94      private transient CellConstraints.Builder constraintsBuilder;
95  
96      /** Stores a reference to the associated platform adapter. */
97      private PercentLayoutPlatformAdapter platformAdapter;
98  
99      /** Stores the column groups. */
100     private Collection<CellGroup> columnGroups;
101 
102     /** Stores the row groups. */
103     private Collection<CellGroup> rowGroups;
104 
105     /** Stores information about components that span multiple columns. */
106     private final List<CellData> multiColumns = new LinkedList<CellData>();
107 
108     /** Stores information about components that span multiple rows. */
109     private final List<CellData> multiRows = new LinkedList<CellData>();
110 
111     /** Stores the total weight factor for all columns. */
112     private int totalWeightX = -1;
113 
114     /** Stores the total weight factor for all rows. */
115     private int totalWeightY = -1;
116 
117     /** Helper flag that avoids re-entrance of the initCells() method. */
118     private volatile boolean inInit;
119 
120     /** A flag whether this layout can shrink below its preferred size. */
121     private boolean canShrink = true;
122 
123     /**
124      * Creates a new, uninitialized instance of <code>PercentLayoutBase</code>.
125      * If this constructor is used, the concrete implementation of the
126      * {@link #initCells(PercentLayoutPlatformAdapter)} method must perform all
127      * initialization.
128      */
129     protected PercentLayoutBase()
130     {
131         super();
132     }
133 
134     /**
135      * Creates a new instance of <code>PercentLayoutBase</code> and sets the
136      * numbers of the rows and columns. The constraints for the cells are set to
137      * default values.
138      *
139      * @param cols the number of columns
140      * @param rows the number of rows
141      */
142     protected PercentLayoutBase(int cols, int rows)
143     {
144         this();
145         initDimensions(cols, rows);
146     }
147 
148     /**
149      * Creates a new instance of <code>PercentLayoutBase</code> and initializes
150      * it. The constraints for the columns and rows are specified in the passed
151      * collections, which must contain instances of
152      * <code>{@link CellConstraints}</code>.
153      *
154      * @param colConstr a collection with column constraints
155      * @param rowConstr a collection with row constraints
156      */
157     protected PercentLayoutBase(
158             Collection<? extends CellConstraints> colConstr,
159             Collection<? extends CellConstraints> rowConstr)
160     {
161         this();
162         if (colConstr == null || colConstr.size() < 1 || rowConstr == null
163                 || rowConstr.size() < 1)
164         {
165             throw new IllegalArgumentException(
166                     "Undefined column or row constraints!");
167         }
168         initFromCollections(colConstr, rowConstr);
169     }
170 
171     /**
172      * Creates a new instance of <code>PercentLayoutBase</code> and initializes
173      * it. The column and row constraints are defined as strings. These strings
174      * must contain valid specifications of cell constraints as defined in the
175      * documentation of <code>{@link CellConstraints}</code>. As separators
176      * between two cell definitions the following characters can be used: &quot;
177      * ,;&quot;.
178      *
179      * @param colConstr a string defining column constraints
180      * @param rowConstr a string defining row constraints
181      */
182     protected PercentLayoutBase(String colConstr, String rowConstr)
183     {
184         this();
185         Collection<CellConstraints> cols = parseConstraints(colConstr, true);
186         Collection<CellConstraints> rows = parseConstraints(rowConstr, false);
187         initFromCollections(cols, rows);
188     }
189 
190     /**
191      * Returns the platform adapter associated with this layout class.
192      *
193      * @return the platform adapter
194      */
195     public PercentLayoutPlatformAdapter getPlatformAdapter()
196     {
197         return platformAdapter;
198     }
199 
200     /**
201      * Sets the platform adapter for this layout manager. This adapter allows
202      * access to and manipulation of the managed components.
203      *
204      * @param platformAdapter the platform adapter to use
205      */
206     public void setPlatformAdapter(PercentLayoutPlatformAdapter platformAdapter)
207     {
208         this.platformAdapter = platformAdapter;
209     }
210 
211     /**
212      * Returns a flag whether this layout can shrink below its preferred size.
213      *
214      * @return a flag whether this layout can shrink below its preferred size
215      */
216     public boolean isCanShrink()
217     {
218         return canShrink;
219     }
220 
221     /**
222      * Sets a flag whether this layout can shrink below its preferred size. If
223      * this flag is set and the space available for the hosting container
224      * becomes smaller than the layout's preferred size, the layout tries to
225      * reduce its size further using the minimum size defined for the components
226      * contained.
227      *
228      * @param canShrink the shrink flag
229      */
230     public void setCanShrink(boolean canShrink)
231     {
232         this.canShrink = canShrink;
233     }
234 
235     /**
236      * Returns the number of columns in this layout.
237      *
238      * @return the number of columns
239      */
240     public int getColumnCount()
241     {
242         ensureInit();
243         return columnConstraints.length;
244     }
245 
246     /**
247      * Returns the number of rows in this layout.
248      *
249      * @return the number of rows
250      */
251     public int getRowCount()
252     {
253         ensureInit();
254         return rowConstraints.length;
255     }
256 
257     /**
258      * Returns the column constraints object for the column with the given
259      * index.
260      *
261      * @param idx the index (0 based)
262      * @return the column constraints object for this column
263      */
264     public CellConstraints getColumnConstraints(int idx)
265     {
266         return getInternalAllColumnConstraints()[idx];
267     }
268 
269     /**
270      * Sets the column constraints object for the column with the given index.
271      *
272      * @param idx the index of the column (0 based)
273      * @param cc the constraints object
274      */
275     public void setColumnConstraints(int idx, CellConstraints cc)
276     {
277         columnConstraints[idx] = cc;
278         totalWeightX = -1;
279     }
280 
281     /**
282      * Returns an array with the current column constraints.
283      *
284      * @return the column constraints
285      */
286     public CellConstraints[] getAllColumnConstraints()
287     {
288         return getInternalAllColumnConstraints().clone();
289     }
290 
291     /**
292      * Returns the row constraints object for the row with the given index.
293      *
294      * @param idx the index (0 based)
295      * @return the row constraints object for this row
296      */
297     public CellConstraints getRowConstraints(int idx)
298     {
299         return getInternalAllRowConstraints()[idx];
300     }
301 
302     /**
303      * Sets the row constraints object for the row with the given index.
304      *
305      * @param idx the index of the row (0 based)
306      * @param cc the constraints object
307      */
308     public void setRowConstraints(int idx, CellConstraints cc)
309     {
310         rowConstraints[idx] = cc;
311         totalWeightY = -1;
312     }
313 
314     /**
315      * Returns an array with the current row constraints.
316      *
317      * @return the row constraints
318      */
319     public CellConstraints[] getAllRowConstraints()
320     {
321         return getInternalAllRowConstraints().clone();
322     }
323 
324     /**
325      * Returns an unmodifiable collection with the column groups defined for
326      * this layout. This collection may be empty, but never <b>null</b>.
327      *
328      * @return a collection with the <code>CellGroup</code> objects for columns
329      */
330     public Collection<CellGroup> getColumnGroups()
331     {
332         ensureInit();
333         return unmodifiableCellGroups(columnGroups);
334     }
335 
336     /**
337      * Adds a <code>CellGroup</code> object for columns to this layout manager.
338      * This causes the columns defined by this group object to have the same
339      * width (as long as their weight factors are equal).
340      *
341      * @param grp the group object
342      */
343     public void addColumnGroup(CellGroup grp)
344     {
345         if (columnGroups == null)
346         {
347             columnGroups = new LinkedList<CellGroup>();
348         }
349         columnGroups.add(grp);
350     }
351 
352     /**
353      * Returns an unmodifiable collection with the row groups defined for this
354      * layout. This collection may be empty, but never <b>null </b>.
355      *
356      * @return a collection with the <code>CellGroup</code> objects for rows
357      */
358     public Collection<CellGroup> getRowGroups()
359     {
360         return unmodifiableCellGroups(rowGroups);
361     }
362 
363     /**
364      * Adds a <code>CellGroup</code> object for rows to this layout manager.
365      * This causes the rows defined by this group object to have the same height
366      * (as long as their weight factors are equal).
367      *
368      * @param grp the group object
369      */
370     public void addRowGroup(CellGroup grp)
371     {
372         if (rowGroups == null)
373         {
374             rowGroups = new LinkedList<CellGroup>();
375         }
376         rowGroups.add(grp);
377     }
378 
379     /**
380      * Clears all cached values. Can be called if something has changed at the
381      * associated container.
382      */
383     public void flushCache()
384     {
385         totalWeightX = -1;
386         totalWeightY = -1;
387         cells = null;
388     }
389 
390     /**
391      * Removes the specified component from this layout.
392      *
393      * @param comp the component to remove
394      * @return a flag whether the component was found and could be removed
395      */
396     public boolean removeComponent(Object comp)
397     {
398         for (int i = 0; i < getColumnCount(); i++)
399         {
400             for (int j = 0; j < getRowCount(); j++)
401             {
402                 Object c = getComponent(i, j);
403                 if (c != null && comp == c)
404                 {
405                     PercentData pd = getPercentData(i, j);
406                     if (pd.getSpanX() > 1)
407                     {
408                         removeMultiSpanComponent(multiColumns, comp);
409                     }
410                     if (pd.getSpanY() > 1)
411                     {
412                         removeMultiSpanComponent(multiRows, comp);
413                     }
414                     cells[i][j] = null;
415                     return true;
416                 }
417             }
418         }
419 
420         return false;
421     }
422 
423     /**
424      * Returns a reference to the associated platform adapter. If no such
425      * adapter has been set, an <code>IllegalStateException</code> exception is
426      * thrown.
427      *
428      * @return the platform adapter
429      * @throws IllegalStateException if no platform adapter is set
430      */
431     protected final PercentLayoutPlatformAdapter fetchPlatformAdapter()
432     {
433         if (getPlatformAdapter() == null)
434         {
435             throw new IllegalStateException(
436                     "No platform adapter set for this percent layout!");
437         }
438         return getPlatformAdapter();
439     }
440 
441     /**
442      * Returns the builder instance for creating {@link CellConstraints}
443      * objects. Each instance of this class is associated with such a builder.
444      * Sub classes or clients can use it for creating their constraints.
445      *
446      * @return the builder instance for creating {@link CellConstraints} objects
447      */
448     public final CellConstraints.Builder getConstraintsBuilder()
449     {
450         if (constraintsBuilder == null)
451         {
452             // create on demand
453             // this is not thread-safe; however, we have stated in the class
454             // comment that the whole class is not thread-safe
455             constraintsBuilder = new CellConstraints.Builder();
456         }
457         return constraintsBuilder;
458     }
459 
460     /**
461      * Initializes the dimensions of the table that holds the layout. Constructs
462      * the internal arrays with the appropriate sizes and initializes them with
463      * default values.
464      *
465      * @param cols the number of columns
466      * @param rows the number of rows
467      */
468     protected final void initDimensions(int cols, int rows)
469     {
470         if (cols < 1 || rows < 1)
471         {
472             throw new IllegalArgumentException(
473                     "Number of columns or rows must be greater 0!");
474         }
475 
476         columnConstraints = new CellConstraints[cols];
477         CellConstraints defColumn = getConstraintsBuilder().defaultColumn()
478                 .create();
479         for (int i = 0; i < cols; i++)
480         {
481             columnConstraints[i] = defColumn;
482         }
483         rowConstraints = new CellConstraints[rows];
484         CellConstraints defRow = getConstraintsBuilder().defaultRow().create();
485         for (int i = 0; i < rows; i++)
486         {
487             rowConstraints[i] = defRow;
488         }
489     }
490 
491     /**
492      * Returns the internal array of all cell constraints objects for the
493      * layout's column. This method can be used by subclasses for direct
494      * read-only access to the layout's column constraints. The public
495      * {@link #getAllColumnConstraints()} method returns a defensive copy of
496      * this array. So for performance reasons this method should be used by
497      * subclasses.
498      *
499      * @return the array with the layout's column constraints objects
500      */
501     protected final CellConstraints[] getInternalAllColumnConstraints()
502     {
503         ensureInit();
504         return columnConstraints;
505     }
506 
507     /**
508      * Returns the internal array of all cell constraints objects for the
509      * layout's rows. This method can be used by subclasses for direct read-only
510      * access to the layout's row constraints. The public
511      * {@link #getAllRowConstraints()} method returns a defensive copy of this
512      * array. So for performance reasons this method should be used by
513      * subclasses.
514      *
515      * @return the array with the layout's row constraints objects
516      */
517     protected final CellConstraints[] getInternalAllRowConstraints()
518     {
519         ensureInit();
520         return rowConstraints;
521     }
522 
523     /**
524      * Returns the component at the specified position of this layout.
525      *
526      * @param col the column
527      * @param row the row
528      * @return the component at this position (<b>null</b> if this cell is not
529      *         occupied)
530      */
531     protected Object getComponent(int col, int row)
532     {
533         CellData cd = getCellData(col, row);
534         return (cd != null) ? cd.getComponent() : null;
535     }
536 
537     /**
538      * Returns the constraints object for the component at the specified
539      * position of this layout.
540      *
541      * @param col the column
542      * @param row the row
543      * @return the constraints at this position (<b>null</b> if this cell is not
544      *         occupied)
545      */
546     protected PercentData getPercentData(int col, int row)
547     {
548         CellData cd = getCellData(col, row);
549         return (cd != null) ? cd.getConstraints() : null;
550     }
551 
552     /**
553      * Checks the specified constraints object. This method ensures that the
554      * passed in constraints object is an instance of {@link PercentData} and
555      * that only valid values for column and row number and the spans are
556      * accepted. If invalid values are detected, an {@code
557      * IllegalStateException} exception is thrown.
558      *
559      * @param constraintsObj the constraints to check
560      * @return the percent data object to use
561      * @throws IllegalStateException if invalid constraints are detected
562      */
563     protected PercentData checkConstraints(Object constraintsObj)
564     {
565         if (!(constraintsObj instanceof PercentData))
566         {
567             throw new IllegalStateException(
568                     "A constraints object of type PercentData must be provided: "
569                             + constraintsObj);
570         }
571 
572         PercentData constraints = (PercentData) constraintsObj;
573         if (constraints.getColumn() + constraints.getSpanX() > getColumnCount()
574                 || constraints.getRow() + constraints.getSpanY() > getRowCount())
575         {
576             throw new IllegalStateException("Invalid column or row span: "
577                     + constraints);
578         }
579 
580         return constraints;
581     }
582 
583     /**
584      * Initializes the specified cell in the table-like layout. Information
585      * about the component and its associated constraints are stored. This
586      * method also checks whether the constrains object is of type
587      * <code>{@link PercentData}</code> and that its indices and dimensions are
588      * valid.
589      *
590      * @param component the component
591      * @param constraints the constraints of this component
592      */
593     protected void initCell(Object component, Object constraints)
594     {
595         PercentData pd = checkConstraints(constraints);
596         CellData cd = new CellData(component, pd);
597         cells[pd.getColumn()][pd.getRow()] = cd;
598     }
599 
600     /**
601      * Resets the states of all cells in the layout. Uses the current column and
602      * row count (which must have been initialized before).
603      */
604     protected void clearCells()
605     {
606         clearCells(getColumnCount(), getRowCount());
607     }
608 
609     /**
610      * Resets the states of all cells in the layout. After this method was
611      * called the layout does not contain any information about components and
612      * their constraints.
613      *
614      * @param columns the number of columns
615      * @param rows the number of rows
616      */
617     protected void clearCells(int columns, int rows)
618     {
619         cells = new CellData[columns][rows];
620     }
621 
622     /**
623      * Returns a list with {@code CellData} objects for the components that span
624      * multiple columns. This method exists mainly for testing purposes.
625      *
626      * @return a list with {@code CellData} objects for multi-column components
627      */
628     List<CellData> getMultiColumnData()
629     {
630         ensureInit();
631         return multiColumns;
632     }
633 
634     /**
635      * Returns a list with {@code CellData} objects for the components that span
636      * multiple rows. This method exists mainly for testing purposes.
637      *
638      * @return a list with {@code CellData} objects for multi-row components
639      */
640     List<CellData> getMultiRowData()
641     {
642         ensureInit();
643         return multiRows;
644     }
645 
646     /**
647      * Calculates the final cell sizes in one direction (horizontal or
648      * vertical). This method takes all factors related to sizing into account:
649      * the cell constraints, cell groups, weight factors, and the shrinking
650      * flag. The resulting array with cell sizes can then be passed to
651      * {@link #performLayout(Object, int[], int[], int[], int[])}.
652      *
653      * @param constraints an array with the constraints for the columns or rows
654      * @param count the number of cells in the opposite dimension (i.e. if
655      *        columns are calculated, the number of rows and vice versa)
656      * @param cellGroups a collection with the cell groups
657      * @param container the container this layout belongs to
658      * @param containerSize the total size available for the container
659      * @param vert a flag if this calculation is for the X or Y direction
660      * @return an array with the cell sizes
661      */
662     public int[] calcSizes(CellConstraints[] constraints, int count,
663             Collection<CellGroup> cellGroups, Object container,
664             int containerSize, boolean vert)
665     {
666         // get cell sizes for preferred size
667         int[] sizes = calcCellSizesWithGroups(constraints, count, cellGroups,
668                 container, false, vert);
669 
670         // is there enough space?
671         int requiredSpace = sumUpSizes(sizes);
672         if (requiredSpace > containerSize && isCanShrink())
673         {
674             // no, try again with minimum size
675             sizes = calcCellSizesWithGroups(constraints, count, cellGroups,
676                     container, true, vert);
677         }
678 
679         // distribute remaining space
680         applyWeightFactors(sizes, containerSize, vert);
681         return sizes;
682     }
683 
684     /**
685      * Layouts all components that are contained in the associated container.
686      * This method determines the positions and sizes of all affected
687      * components.
688      *
689      * @param container the container
690      * @param colSizes an array with the sizes of all columns
691      * @param rowSizes an array with the sizes of all rows
692      * @param colPos the start positions of all columns
693      * @param rowPos the start positions of all rows
694      */
695     public void performLayout(Object container, int[] colSizes, int[] rowSizes,
696             int[] colPos, int[] rowPos)
697     {
698         Rectangle rect = new Rectangle();
699         for (int col = 0; col < getColumnCount(); col++)
700         {
701             for (int row = 0; row < getRowCount(); row++)
702             {
703                 if (getPercentData(col, row) != null)
704                 {
705                     alignComponent(rect, col, row, colSizes, colPos[col], col,
706                             container, false);
707                     alignComponent(rect, col, row, rowSizes, rowPos[row], row,
708                             container, true);
709                     setComponentBounds(col, row, rect);
710                 }
711             }
712         }
713     }
714 
715     /**
716      * Layouts all components in the associated container calculating all
717      * necessary intermediate sizes and positions. This method calculates the
718      * cell sizes and positions and then delegates to the overloaded
719      * {@code performLayout()} method.
720      *
721      * @param container the container
722      * @param insets a rectangle with the container's insets
723      * @param size the size of the container
724      * @since 1.3
725      */
726     public void performLayout(Object container, Rectangle insets, Dimension size)
727     {
728         int[] colSizes =
729                 calcSizes(getAllColumnConstraints(), getRowCount(),
730                         getColumnGroups(), container, size.width - insets.x
731                                 - insets.width, false);
732         int[] rowSizes =
733                 calcSizes(getAllRowConstraints(), getColumnCount(),
734                         getRowGroups(), container, size.height - insets.y
735                                 - insets.width, true);
736         int[] colPos = calcCellPositions(colSizes, insets.x);
737         int[] rowPos = calcCellPositions(rowSizes, insets.y);
738         performLayout(container, colSizes, rowSizes, colPos, rowPos);
739     }
740 
741     /**
742      * Returns the preferred size of this layout. This method applies all cell
743      * constraints to determine the optimum size of the layout.
744      *
745      * @param container the associated container object
746      * @return the preferred layout size
747      */
748     public Dimension calcPreferredLayoutSize(Object container)
749     {
750         return calcLayoutSize(container, false);
751     }
752 
753     /**
754      * Returns the minimum size of this layout. The behavior of this method
755      * depends on the {@code canShrink} flag: if this flag is <b>false</b>, it
756      * returns the same result as {@link #calcPreferredLayoutSize(Object)} -
757      * because the layout cannot shrink below its preferred size. Otherwise, a
758      * size calculation is performed based on the component's minimum size
759      * rather than their preferred sizes. Note that this mainly makes a
760      * difference if cell constraints are used with the {@link CellSize}
761      * <em>preferred</em>.
762      *
763      * @param container the associated container object
764      * @return the minimum layout size
765      */
766     public Dimension calcMinimumLayoutSize(Object container)
767     {
768         return calcLayoutSize(container, isCanShrink());
769     }
770 
771     /**
772      * Calculates either the preferred or the minimum layout size.
773      *
774      * @param container the associated container object
775      * @param minimum flag for minimum (<b>true</b>) or preferred (<b>false</b>)
776      *        size
777      * @return the corresponding layout size
778      */
779     private Dimension calcLayoutSize(Object container, boolean minimum)
780     {
781         int[] colSizes = calcCellSizesWithGroups(getInternalAllColumnConstraints(),
782                 getRowCount(), getColumnGroups(), container, minimum, false);
783         int[] rowSizes = calcCellSizesWithGroups(getInternalAllRowConstraints(),
784                 getColumnCount(), getRowGroups(), container, minimum, true);
785         return new Dimension(sumUpSizes(colSizes), sumUpSizes(rowSizes));
786     }
787 
788     /**
789      * Initializes this instance from the given collections with
790      * {@link CellConstraints} objects.
791      *
792      * @param colConstr a collection with column constraints object
793      * @param rowConstr a collection with row constraints object
794      */
795     protected final void initFromCollections(
796             Collection<? extends CellConstraints> colConstr,
797             Collection<? extends CellConstraints> rowConstr)
798     {
799         columnConstraints = copyConstraints(colConstr);
800         rowConstraints = copyConstraints(rowConstr);
801     }
802 
803     /**
804      * Calculates the minimum size of either the columns or the rows in the
805      * layout. Because of the passed in orientation flag this method can operate
806      * on both columns and rows.
807      *
808      * @param constraints an array with the constraints for the columns or rows
809      * @param count the number of cells in the opposite dimension (i.e. if
810      *        columns are calculated, the number of rows and vice versa)
811      * @param container the container this layout belongs to
812      * @param minimum a flag whether the minimum size should be returned
813      * @param vert a flag if this calculation is for the X or Y direction
814      * @return an array with the cell sizes
815      */
816     protected int[] calcCellSizes(CellConstraints[] constraints, int count,
817             Object container, boolean minimum, boolean vert)
818     {
819         int[] sizes = new int[constraints.length];
820         for (int i = 0; i < constraints.length; i++)
821         {
822             sizes[i] = calcComponentSizes(constraints[i], i, count, container,
823                     minimum, vert);
824         }
825 
826         handleMultiSpans(sizes, constraints, vert ? multiRows : multiColumns,
827                 container, minimum, vert);
828         return sizes;
829     }
830 
831     /**
832      * Calculates the size of either a column or a row in the layout. This
833      * method iterates over all the components in the actual column or row.
834      * Depending on their constraints either their minimum, their preferred or a
835      * specified fix size is fetched, and the maximum of these sizes is
836      * determined. Only components with a span of 1 are taken into account. With
837      * the {@code minimum} parameter it is possible to force the method to
838      * always return the minimum size. This is required if there is less space
839      * available than is required for the preferred width.
840      *
841      * @param constraints the constraints object for the actual column or row
842      * @param index the index of the actual column or row
843      * @param count the number of cells in the opposite dimension
844      * @param container the container this layout belongs to
845      * @param minimum a flag whether the minimum size should be returned
846      * @param vert a flag if this calculation is for the X or Y direction
847      * @return the size of the actual column or row
848      */
849     protected int calcComponentSizes(CellConstraints constraints, int index,
850             int count, Object container, boolean minimum, boolean vert)
851     {
852         int size = constraints.getMinSize().toPixel(getSizeHandler(),
853                 container, vert);
854         int colIdx = 0;
855         int rowIdx = 0;
856         if (vert)
857         {
858             rowIdx = index;
859         }
860         else
861         {
862             colIdx = index;
863         }
864 
865         for (int i = 0; i < count; i++)
866         {
867             if (vert)
868             {
869                 colIdx = i;
870             }
871             else
872             {
873                 rowIdx = i;
874             }
875             PercentData pd = getPercentData(colIdx, rowIdx);
876             if (pd != null
877                     && 1 == getOrientationValue(pd.getSpanX(), pd.getSpanY(),
878                             vert))
879             {
880                 int cellSize = calcCellSize(pd, colIdx, rowIdx, container,
881                         minimum, vert);
882                 if (cellSize > size)
883                 {
884                     size = cellSize;
885                 }
886             }
887         }
888 
889         return size;
890     }
891 
892     /**
893      * Checks the sizes of components that span multiple cells. For these cells
894      * it must be tested whether their size fits into the cell sizes so far
895      * calculated. If this is not the case, cells, for which this is possible,
896      * must be enlarged.
897      *
898      * @param sizes an array with the so far calculated cell sizes
899      * @param constraints an array with all cell constraints
900      * @param components the list with the multi span components
901      * @param container the container object
902      * @param minimum a flag whether the minimum size should be returned
903      * @param vert the orientation flag
904      */
905     protected void handleMultiSpans(int[] sizes, CellConstraints[] constraints,
906             List<CellData> components, Object container, boolean minimum,
907             boolean vert)
908     {
909         for (CellData cd : components)
910         {
911             PercentData pd = cd.getConstraints();
912             int size = calcComponentSize(pd, cd.getComponent(), container,
913                     minimum, vert);
914             int idx1 = getOrientationValue(pd.getColumn(), pd.getRow(), vert);
915             int idx2 = idx1
916                     + getOrientationValue(pd.getSpanX(), pd.getSpanY(), vert);
917 
918             // determine available size
919             int cellSize = 0;
920             for (int i = idx1; i < idx2; i++)
921             {
922                 cellSize += sizes[i];
923             }
924 
925             if (size > cellSize)
926             {
927                 enlargeCells(sizes, constraints, idx1, idx2, size - cellSize);
928             }
929         }
930     }
931 
932     /**
933      * Enlarges the cells in the specified index range by the given amount. This
934      * method determines how many cells can be enlarged (by inspecting their
935      * cell constraints). If there are any, they will be enlarged by the same
936      * part.
937      *
938      * @param sizes an array with the so far calculated cell sizes
939      * @param constraints the constraints
940      * @param idx1 the first index
941      * @param idx2 the end index (excluding)
942      * @param amount the enlargement amount
943      */
944     private void enlargeCells(int[] sizes, CellConstraints[] constraints,
945             int idx1, int idx2, int amount)
946     {
947         int cnt = 0;
948         for (int i = idx1; i < idx2; i++)
949         {
950             if (constraints[i].getCellSize() != CellSize.NONE)
951             {
952                 cnt++;
953             }
954         }
955 
956         if (cnt > 0)
957         {
958             int factor = amount / cnt;
959             int modulo = amount % cnt;
960             for (int i = idx1; i < idx2 && cnt > 0; i++)
961             {
962                 if (constraints[i].getCellSize() != CellSize.NONE)
963                 {
964                     sizes[i] += factor + ((modulo-- > 0) ? 1 : 0);
965                     cnt--;
966                 }
967             }
968         }
969     }
970 
971     /**
972      * Determines the size of a single cell. Evaluates the constraints of this
973      * cell and depending on the size value either the minimum, the preferred or
974      * a fixed size is returned.
975      *
976      * @param pd the constraints object
977      * @param colIdx the column index
978      * @param rowIdx the row index
979      * @param container the container this layout belongs to
980      * @param minimum a flag whether the minimum size should be returned
981      * @param vert a flag if this calculation is for the X or Y direction
982      * @return the cell's size
983      */
984     protected int calcCellSize(PercentData pd, int colIdx, int rowIdx,
985             Object container, boolean minimum, boolean vert)
986     {
987         return calcComponentSize(pd, getComponent(colIdx, rowIdx), container,
988                 minimum, vert);
989     }
990 
991     /**
992      * Determines the size of a component based on the given constraints object.
993      *
994      * @param pd the constraints object
995      * @param comp the affected component
996      * @param container the container this layout belongs to
997      * @param minimum a flag whether the minimum size should be returned
998      * @param vert a flag if this calculation is for the X or Y direction
999      * @return the component's size
1000      */
1001     protected int calcComponentSize(PercentData pd, Object comp,
1002             Object container, boolean minimum, boolean vert)
1003     {
1004         CellConstraints constr = constraintsFor(pd, vert);
1005         int sz;
1006 
1007         if (constr.getCellSize() == CellSize.NONE)
1008         {
1009             sz = 0;
1010         }
1011         else if (minimum || constr.getCellSize() == CellSize.MINIMUM)
1012         {
1013             sz = fetchPlatformAdapter().getMinimumComponentSize(comp, vert);
1014         }
1015         else
1016         {
1017             sz = fetchPlatformAdapter().getPreferredComponentSize(comp, vert);
1018         }
1019 
1020         return Math.max(sz, constr.getMinSize().toPixel(getSizeHandler(),
1021                 container, vert));
1022     }
1023 
1024     /**
1025      * Applies the defined cell groups to the so far calculated cell sizes.
1026      *
1027      * @param sizes an array with the (initial or minimum) cell sizes
1028      * @param cellGroups a collection with the defined cell groups
1029      */
1030     protected void applyCellGroups(int[] sizes, Collection<CellGroup> cellGroups)
1031     {
1032         for (CellGroup group : cellGroups)
1033         {
1034             group.apply(sizes);
1035         }
1036     }
1037 
1038     /**
1039      * Calculates the sizes of all columns or rows. This is a convenience
1040      * method, which combines calls to <code>calcCellSizes()</code> and
1041      * <code>applyCellGroups()</code>.
1042      *
1043      * @param constraints an array with the constraints for the columns or rows
1044      * @param count the number of cells in the opposite dimension (i.e. if
1045      *        columns are calculated, the number of rows and vice versa)
1046      * @param cellGroups a collection with the defined cell groups
1047      * @param container the container this layout belongs to
1048      * @param minimum a flag whether the minimum size should be returned
1049      * @param vert a flag if this calculation is for the X or Y direction
1050      * @return an array with the cell sizes
1051      * @throws NullPointerException if a required parameter is missing
1052      */
1053     protected int[] calcCellSizesWithGroups(CellConstraints[] constraints,
1054             int count, Collection<CellGroup> cellGroups, Object container,
1055             boolean minimum, boolean vert)
1056     {
1057         int[] sizes = calcCellSizes(constraints, count, container, minimum, vert);
1058         applyCellGroups(sizes, cellGroups);
1059         return sizes;
1060     }
1061 
1062     /**
1063      * Helper method for calculating the total weight factor.
1064      *
1065      * @param constraints an array with cell constraints
1066      * @return the total weight factor
1067      */
1068     protected int calcTotalWeight(CellConstraints[] constraints)
1069     {
1070         int result = 0;
1071         for (CellConstraints cc : constraints)
1072         {
1073             result += cc.getWeight();
1074         }
1075         return result;
1076     }
1077 
1078     /**
1079      * Returns the total weight factor for columns.
1080      *
1081      * @return the total column weight
1082      */
1083     protected int getTotalWeightX()
1084     {
1085         if (totalWeightX < 0)
1086         {
1087             totalWeightX = calcTotalWeight(columnConstraints);
1088         }
1089         return totalWeightX;
1090     }
1091 
1092     /**
1093      * Returns the total weight factor for rows.
1094      *
1095      * @return the total row weight
1096      */
1097     protected int getTotalWeightY()
1098     {
1099         if (totalWeightY < 0)
1100         {
1101             totalWeightY = calcTotalWeight(rowConstraints);
1102         }
1103         return totalWeightY;
1104     }
1105 
1106     /**
1107      * Processes the cells with a weight factor larger than 0. The remaining
1108      * available space is calculated and divided between the cells with a
1109      * defined weight factor.
1110      *
1111      * @param sizes the cell sizes without weight factors
1112      * @param containerSize the size of the container (without insets)
1113      * @param constraints the cell constraints
1114      * @param totalWeight the total weight factor
1115      */
1116     protected void applyWeightFactors(int[] sizes, int containerSize,
1117             CellConstraints[] constraints, int totalWeight)
1118     {
1119         if (totalWeight > 0)
1120         {
1121             int remaining = containerSize - sumUpSizes(sizes);
1122             if (remaining > 0)
1123             {
1124                 for (int i = 0; i < constraints.length; i++)
1125                 {
1126                     if (constraints[i].getWeight() > 0)
1127                     {
1128                         sizes[i] += (remaining * constraints[i].getWeight())
1129                                 / totalWeight;
1130                     }
1131                 }
1132             }
1133         }
1134     }
1135 
1136     /**
1137      * Processes the cells with a weight factor larger than 0. The remaining
1138      * available space is calculated and divided between the cells with a
1139      * defined weight factor.
1140      *
1141      * @param sizes the cell sizes without weight factors
1142      * @param containerSize the size of the container (without insets)
1143      * @param vert a flag if this calculation is for the X or Y direction
1144      */
1145     protected void applyWeightFactors(int[] sizes, int containerSize,
1146             boolean vert)
1147     {
1148         if (vert)
1149         {
1150             applyWeightFactors(sizes, containerSize, rowConstraints,
1151                     getTotalWeightY());
1152         }
1153         else
1154         {
1155             applyWeightFactors(sizes, containerSize, columnConstraints,
1156                     getTotalWeightX());
1157         }
1158     }
1159 
1160     /**
1161      * Calculates the start positions of all cells in a column or row.
1162      *
1163      * @param sizes the cell sizes (must not be <b>null</b>)
1164      * @param startPos the start position
1165      * @return an array with the start positions for all cells
1166      * @throws NullPointerException if the array with sizes is <b>null</b>
1167      */
1168     public int[] calcCellPositions(int[] sizes, int startPos)
1169     {
1170         int[] pos = new int[sizes.length];
1171         int p = startPos;
1172         for (int i = 0; i < sizes.length; p += sizes[i++])
1173         {
1174             pos[i] = p;
1175         }
1176         return pos;
1177     }
1178 
1179     /**
1180      * Aligns the specified component.
1181      *
1182      * @param bounds stores the bounds of the component
1183      * @param colIdx the column index
1184      * @param rowIdx the row index
1185      * @param sizes an array with the sizes of all cells
1186      * @param startPos the start position of the actual cell
1187      * @param idx the actual cell index
1188      * @param container the container this layout belongs to
1189      * @param vert a flag if this calculation is for the X or Y direction
1190      */
1191     protected void alignComponent(Rectangle bounds, int colIdx, int rowIdx,
1192             int[] sizes, int startPos, int idx, Object container, boolean vert)
1193     {
1194         // Determine available space, including cell spanning
1195         PercentData pd = getPercentData(colIdx, rowIdx);
1196         int span = getOrientationValue(pd.getSpanX(), pd.getSpanY(), vert);
1197         int availSpace = 0;
1198         for (int i = 0; i < span; i++)
1199         {
1200             availSpace += sizes[idx + i];
1201         }
1202 
1203         // Determine alignment
1204         int pos;
1205         int cellSize;
1206         if (constraintsFor(pd, vert).getAlignment() == CellAlignment.FULL)
1207         {
1208             // component fills the whole area
1209             pos = 0;
1210             cellSize = availSpace;
1211         }
1212 
1213         else
1214         {
1215             cellSize = calcCellSize(pd, colIdx, rowIdx, container, false, vert);
1216             CellAlignment align = constraintsFor(pd, vert).getAlignment();
1217             if (align == CellAlignment.CENTER)
1218             {
1219                 pos = (availSpace - cellSize) >> 1;
1220             }
1221             else if (align == CellAlignment.END)
1222             {
1223                 pos = availSpace - cellSize;
1224             }
1225             else
1226             {
1227                 pos = 0;
1228             }
1229         }
1230 
1231         if (vert)
1232         {
1233             bounds.y = startPos + pos;
1234             bounds.height = cellSize;
1235         }
1236         else
1237         {
1238             bounds.x = startPos + pos;
1239             bounds.width = cellSize;
1240         }
1241     }
1242 
1243     /**
1244      * Helper method for copying a collection with constraints objects into an
1245      * array. If a constraints object is <b>null </b>, an exception is thrown.
1246      *
1247      * @param constr the collection with the constraints
1248      * @return an array with constraints
1249      * @throws IllegalArgumentException if the collection contains a <b>null</b>
1250      *         constraint
1251      */
1252     private static CellConstraints[] copyConstraints(
1253             Collection<? extends CellConstraints> constr)
1254     {
1255         CellConstraints[] result = new CellConstraints[constr.size()];
1256         Iterator<? extends CellConstraints> it = constr.iterator();
1257         for (int idx = 0; it.hasNext(); idx++)
1258         {
1259             result[idx] = it.next();
1260             if (result[idx] == null)
1261             {
1262                 throw new IllegalArgumentException(
1263                         "Cell constraints must not be null!");
1264             }
1265         }
1266         return result;
1267     }
1268 
1269     /**
1270      * Helper method for parsing a string with cell definitions.
1271      *
1272      * @param constr the string
1273      * @param col a flag if column or row definitions are to be parsed
1274      * @return a collection with the corresponding constraints objects
1275      * @throws IllegalArgumentException if the string is invalid
1276      */
1277     private Collection<CellConstraints> parseConstraints(String constr,
1278             boolean col)
1279     {
1280         CellAlignment oldAlign = getConstraintsBuilder().getDefaultAlignment();
1281         getConstraintsBuilder().setDefaultAlignment(
1282                 col ? CellAlignment.FULL : CellAlignment.CENTER);
1283 
1284         try
1285         {
1286             getConstraintsBuilder().reset();
1287             Collection<CellConstraints> result = new LinkedList<CellConstraints>();
1288             if (constr != null)
1289             {
1290                 StringTokenizer tok = new StringTokenizer(constr,
1291                         CONSTRAINTS_DELIMITERS);
1292                 while (tok.hasMoreTokens())
1293                 {
1294                     result.add(getConstraintsBuilder().fromString(
1295                             tok.nextToken()));
1296                 }
1297             }
1298 
1299             if (result.size() < 1)
1300             {
1301                 throw new IllegalArgumentException(
1302                         "Undefined cell constraints!");
1303             }
1304             return result;
1305         }
1306         finally
1307         {
1308             getConstraintsBuilder().setDefaultAlignment(oldAlign);
1309         }
1310     }
1311 
1312     /**
1313      * Helper method for extracting a value from a 2D vector with the specified
1314      * orientation.
1315      *
1316      * @param v1 the x value
1317      * @param v2 the y value
1318      * @param vert the orientation flag (<b>true</b> for the y value,
1319      *        <b>false</b> for the x value)
1320      * @return the extracted value
1321      */
1322     public static int getOrientationValue(int v1, int v2, boolean vert)
1323     {
1324         return vert ? v2 : v1;
1325     }
1326 
1327     /**
1328      * Returns the minimum size of the component at the specified column and row
1329      * position in the given orientation.
1330      *
1331      * @param col the column index
1332      * @param row the row index
1333      * @param vert the orientation flag
1334      * @return the minimum size of the specified component
1335      */
1336     protected int getMinimumComponentSize(int col, int row, boolean vert)
1337     {
1338         return fetchPlatformAdapter().getMinimumComponentSize(
1339                 getComponent(col, row), vert);
1340     }
1341 
1342     /**
1343      * Returns the preferred size of the component at the specified column and
1344      * row position in the given orientation.
1345      *
1346      * @param col the column index
1347      * @param row the row index
1348      * @param vert the orientation flag
1349      * @return the minimum size of the specified component
1350      */
1351     protected int getPreferredComponentSize(int col, int row, boolean vert)
1352     {
1353         return fetchPlatformAdapter().getPreferredComponentSize(
1354                 getComponent(col, row), vert);
1355     }
1356 
1357     /**
1358      * Sets the bounds of the component at the specified column and row
1359      * position. This method is called after the exact position of this
1360      * component has been determined by the layout algorithm.
1361      *
1362      * @param col the column index
1363      * @param row the row index
1364      * @param bounds the new bounds for this component
1365      */
1366     protected void setComponentBounds(int col, int row, Rectangle bounds)
1367     {
1368         fetchPlatformAdapter().setComponentBounds(getComponent(col, row),
1369                 bounds);
1370     }
1371 
1372     /**
1373      * Returns the currently used size handler implementation.
1374      *
1375      * @return the size handler implementation
1376      */
1377     protected UnitSizeHandler getSizeHandler()
1378     {
1379         return fetchPlatformAdapter().getSizeHandler();
1380     }
1381 
1382     /**
1383      * Returns the constraints object for the specified percent data and the
1384      * given orientation. If the percent data contains already a constraints
1385      * object for the given orientation, this object is returned. Otherwise the
1386      * constraints of the hosting cell (the target cell) are returned.
1387      *
1388      * @param pd the percent data object
1389      * @param vert the orientation flag
1390      * @return the constraints for this percent data object
1391      */
1392     protected CellConstraints constraintsFor(PercentData pd, boolean vert)
1393     {
1394         CellConstraints c = pd.getConstraints(vert);
1395 
1396         if (c == null)
1397         {
1398             c = vert ? getRowConstraints(targetRow(pd))
1399                     : getColumnConstraints(targetColumn(pd));
1400         }
1401 
1402         return c;
1403     }
1404 
1405     /**
1406      * Initializes the whole layout. This method is called on first access to
1407      * the layout information or whenever the layout changes. A concrete sub
1408      * class must here implement its initialization algorithm, which creates a
1409      * valid percent layout.
1410      *
1411      * @param adapter the currently used platform adapter
1412      */
1413     protected abstract void initCells(PercentLayoutPlatformAdapter adapter);
1414 
1415     /**
1416      * Determines the target column of the given constraints object. If a target
1417      * column is set, it is used. Otherwise the column of the data object is
1418      * returned.
1419      *
1420      * @param pd the {@code PercentData} object
1421      * @return the target column
1422      */
1423     private static int targetColumn(PercentData pd)
1424     {
1425         return (pd.getTargetColumn() <= PercentData.POS_UNDEF) ? pd.getColumn()
1426                 : pd.getTargetColumn();
1427     }
1428 
1429     /**
1430      * Determines the target row of the given constraints object. If a target
1431      * row is set, it is used. Otherwise the row of the data object is returned.
1432      *
1433      * @param pd the {@code PercentData} object
1434      * @return the target row
1435      */
1436     private static int targetRow(PercentData pd)
1437     {
1438         return (pd.getTargetRow() <= PercentData.POS_UNDEF) ? pd.getRow() : pd
1439                 .getTargetRow();
1440     }
1441 
1442     /**
1443      * Returns the <code>CellData</code> object for the specified cell. Ensures
1444      * that the cells have been initialized.
1445      *
1446      * @param col the column
1447      * @param row the row
1448      * @return the cell data object at this position (can be <b>null</b>)
1449      */
1450     private CellData getCellData(int col, int row)
1451     {
1452         ensureInit();
1453         return cells[col][row];
1454     }
1455 
1456     /**
1457      * Ensures that the layout has been initialized. This method is called by
1458      * several accessor methods.
1459      */
1460     private void ensureInit()
1461     {
1462         if (!inInit && cells == null)
1463         {
1464             inInit = true;
1465             try
1466             {
1467                 initCells(fetchPlatformAdapter());
1468                 initMultiCells();
1469             }
1470             finally
1471             {
1472                 inInit = false;
1473             }
1474         }
1475     }
1476 
1477     /**
1478      * Helper method for obtaining information about components that span
1479      * multiple cells. If these components are associated a target cell, they
1480      * are stored at a special location.
1481      */
1482     private void initMultiCells()
1483     {
1484         int cols = getColumnCount();
1485         int rows = getRowCount();
1486         multiColumns.clear();
1487         multiRows.clear();
1488 
1489         for (int i = 0; i < cols; i++)
1490         {
1491             for (int j = 0; j < rows; j++)
1492             {
1493                 PercentData pd = getPercentData(i, j);
1494                 if (pd != null)
1495                 {
1496                     if (pd.getSpanX() > 1)
1497                     {
1498                         multiColumns.add(getCellData(i, j));
1499                     }
1500                     if (pd.getSpanY() > 1)
1501                     {
1502                         multiRows.add(getCellData(i, j));
1503                     }
1504                 }
1505             }
1506         }
1507     }
1508 
1509     /**
1510      * Calculates the width or the height of the associated component. This
1511      * method calculates the sum of the already determined cell sizes.
1512      *
1513      * @param sizes an array with the cell sizes
1514      * @return the layout size
1515      */
1516     private static int sumUpSizes(int[] sizes)
1517     {
1518         int result = 0;
1519         for (int sz : sizes)
1520         {
1521             result += sz;
1522         }
1523 
1524         return result;
1525     }
1526 
1527     /**
1528      * Removes a component from the multi span list.
1529      *
1530      * @param lst the multi span list
1531      * @param comp the component to remove
1532      */
1533     private static void removeMultiSpanComponent(List<CellData> lst, Object comp)
1534     {
1535         for (Iterator<CellData> it = lst.iterator(); it.hasNext();)
1536         {
1537             CellData cd = it.next();
1538             if (cd.getComponent() == comp)
1539             {
1540                 it.remove();
1541                 break;
1542             }
1543         }
1544     }
1545 
1546     /**
1547      * Helper method for returning an unmodifiable collection of cell groups.
1548      *
1549      * @param groups the source collection with cell groups
1550      * @return the unmodifiable collection
1551      */
1552     private static Collection<CellGroup> unmodifiableCellGroups(
1553             Collection<CellGroup> groups)
1554     {
1555         if (groups == null)
1556         {
1557             return Collections.emptyList();
1558         }
1559         else
1560         {
1561             return Collections.unmodifiableCollection(groups);
1562         }
1563     }
1564 
1565     /**
1566      * A helper class for storing information about a cell in the layout.
1567      */
1568     static class CellData
1569     {
1570         /** Stores the component that occupies this cell. */
1571         private final Object component;
1572 
1573         /** Stores the percent data object for this cell. */
1574         private final PercentData constraints;
1575 
1576         /**
1577          * Creates a new instance of <code>CellData</code> and initializes it.
1578          *
1579          * @param comp the component
1580          * @param constr the constraints
1581          */
1582         public CellData(Object comp, PercentData constr)
1583         {
1584             component = comp;
1585             constraints = constr;
1586         }
1587 
1588         /**
1589          * Returns the component stored in this cell.
1590          *
1591          * @return the component
1592          */
1593         public Object getComponent()
1594         {
1595             return component;
1596         }
1597 
1598         /**
1599          * Returns the constraints.
1600          *
1601          * @return the constraints
1602          */
1603         public PercentData getConstraints()
1604         {
1605             return constraints;
1606         }
1607     }
1608 }