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: "
177 * ,;".
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 }