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.io.Serializable;
19  
20  import org.apache.commons.lang.ObjectUtils;
21  
22  /**
23   * <p>
24   * A constraints class used by {@link PercentLayout}.
25   * </p>
26   * <p>
27   * This class is used to define data needed by the percent layout manager when
28   * new components are added to the managed container. The main data stored in
29   * instances of this class is information about where in the logic grid
30   * maintained by the layout manager the new component should be placed, i.e. the
31   * coordinates of the cell and the number of cells in X and Y direction that are
32   * spanned.
33   * </p>
34   * <p>
35   * It is also possible to associate a <code>PercentData</code> object with row
36   * and column constraints. If these constraints are defined, they override the
37   * constraints set for the occupied column and row. This makes it possible e.g.
38   * to set a different alignment for a certain component, while the other
39   * components in this row or column use the default alignment.
40   * </p>
41   * <p>
42   * Another information stored in instances of this class is the so-called
43   * <em>target cell</em>. This information is evaluated only if multiple columns
44   * or rows are occupied. In this case the target column determines to which
45   * column the size of the associated component should be assigned. This is
46   * optional; if no target column is set, the associated component's width will
47   * not be taken into account when the minimum layout width is calculated. The
48   * same applies for the target row.
49   * </p>
50   * <p>
51   * Instances of this class are immutable and thus thread-safe. They are not
52   * created directly, but the nested {@code Builder} class is used for this
53   * purpose. A typical sequence for creating {@code PercentData} objects could
54   * look as follows:
55   *
56   * <pre>
57   * PercentData.Builder b = new PercentData.Builder();
58   * PercentData pd1 = b.xy(1, 2).create();
59   * PercentData pd2 = b.xy(1, 3).spanX(2).withColumnConstraints(columnConstr).create();
60   * </pre>
61   *
62   * </p>
63   *
64   * @see net.sf.jguiraffe.gui.layout.CellConstraints
65   * @author Oliver Heger
66   * @version $Id: PercentData.java 205 2012-01-29 18:29:57Z oheger $
67   */
68  public final class PercentData implements Serializable
69  {
70      /** Constant for an undefined cell position. */
71      public static final int POS_UNDEF = -1;
72  
73      /**
74       * The serial version UID.
75       */
76      private static final long serialVersionUID = 20090730L;
77  
78      /** Constant for the initial buffer size. */
79      private static final int BUF_SIZE = 64;
80  
81      /** Stores the column. */
82      private final int column;
83  
84      /** Stores the row. */
85      private final int row;
86  
87      /** Stores the number of occupied columns. */
88      private final int spanX;
89  
90      /** Stores the number of occupied rows. */
91      private final int spanY;
92  
93      /** Stores the target column. */
94      private final int targetColumn;
95  
96      /** Stores the targetRow. */
97      private final int targetRow;
98  
99      /** Stores a reference to a constraints object for the column. */
100     private final CellConstraints columnConstraints;
101 
102     /** Stores a reference to a constraints object for the row. */
103     private final CellConstraints rowConstraints;
104 
105     /**
106      * Creates a new instance of {@code PercentData} and initializes all
107      * properties. This constructor is called by the builder.
108      *
109      * @param x the column index
110      * @param y the row index
111      * @param w the span in X direction
112      * @param h the span in Y direction
113      * @param tx the target column
114      * @param ty the target row
115      * @param cccol the column constraints
116      * @param ccrow the row constraints
117      */
118     private PercentData(int x, int y, int w, int h, int tx, int ty,
119             CellConstraints cccol, CellConstraints ccrow)
120     {
121         column = x;
122         row = y;
123         spanX = w;
124         spanY = h;
125         targetColumn = tx;
126         targetRow = ty;
127         columnConstraints = cccol;
128         rowConstraints = ccrow;
129     }
130 
131     /**
132      * Returns the number of the column, in which the corresponding component is
133      * to be placed.
134      *
135      * @return the column number
136      */
137     public int getColumn()
138     {
139         return column;
140     }
141 
142     /**
143      * Returns the number of the row, in which the corresponding component it to
144      * be placed.
145      *
146      * @return the row number
147      */
148     public int getRow()
149     {
150         return row;
151     }
152 
153     /**
154      * Returns the number of occupied columns.
155      *
156      * @return the number of occupied columns
157      */
158     public int getSpanX()
159     {
160         return spanX;
161     }
162 
163     /**
164      * Returns the number of occupied rows.
165      *
166      * @return the number of occupied rows
167      */
168     public int getSpanY()
169     {
170         return spanY;
171     }
172 
173     /**
174      * Returns the target column.
175      *
176      * @return the target column
177      */
178     public int getTargetColumn()
179     {
180         return targetColumn;
181     }
182 
183     /**
184      * Returns the target row.
185      *
186      * @return the target row
187      */
188     public int getTargetRow()
189     {
190         return targetRow;
191     }
192 
193     /**
194      * Returns the associated constraints object for the column.
195      *
196      * @return the column constraints object (may be <b>null </b>)
197      */
198     public CellConstraints getColumnConstraints()
199     {
200         return columnConstraints;
201     }
202 
203     /**
204      * Returns the associated constraints object for the row.
205      *
206      * @return the row constraints object (may be <b>null </b>)
207      */
208     public CellConstraints getRowConstraints()
209     {
210         return rowConstraints;
211     }
212 
213     /**
214      * A convenience method for determining the cell constraints object for the
215      * specified orientation. This method is useful for some generic methods in
216      * <code>PercentLayoutBase</code> that can operate either on columns or on
217      * rows.
218      *
219      * @param vert the orientation flag (<b>true</b> for vertical, <b>false</b>
220      *        for horizontal)
221      * @return the corresponding constraints object
222      */
223     public CellConstraints getConstraints(boolean vert)
224     {
225         return vert ? getRowConstraints() : getColumnConstraints();
226     }
227 
228     /**
229      * Appends a string representation of this object to the specified string
230      * buffer. This string contains the properties of this instance with their
231      * values. No further information (e.g. the class name) is written.
232      *
233      * @param buf the target buffer (must not be <b>null</b>)
234      * @throws IllegalArgumentException if the buffer is <b>null</b>
235      */
236     public void buildString(StringBuilder buf)
237     {
238         if (buf == null)
239         {
240             throw new IllegalArgumentException("Buffer must not be null!");
241         }
242 
243         buf.append("COL = ").append(getColumn());
244         buf.append(" ROW = ").append(getRow());
245         buf.append(" SPANX = ").append(getSpanX());
246         buf.append(" SPANY = ").append(getSpanY());
247         if (getTargetColumn() > POS_UNDEF)
248         {
249             buf.append(" TARGETCOL = ").append(getTargetColumn());
250         }
251         if (getTargetRow() > POS_UNDEF)
252         {
253             buf.append(" TARGETROW = ").append(getTargetRow());
254         }
255         if (getColumnConstraints() != null)
256         {
257             buf.append(" COLCONSTR = ").append(
258                     getColumnConstraints().toSpecificationString());
259         }
260         if (getRowConstraints() != null)
261         {
262             buf.append(" ROWCONSTR = ").append(
263                     getRowConstraints().toSpecificationString());
264         }
265     }
266 
267     /**
268      * Returns a string representation of this object.
269      *
270      * @return a string representation
271      */
272     @Override
273     public String toString()
274     {
275         StringBuilder buf = new StringBuilder(BUF_SIZE);
276         buf.append(getClass().getName());
277         buf.append(" [ ");
278         buildString(buf);
279         buf.append(" ]");
280         return buf.toString();
281     }
282 
283     /**
284      * Compares this object with another one. Two instances of {@code
285      * PercentData} are considered equal if all of their properties are equal.
286      *
287      * @param obj the object to compare to
288      * @return a flag whether these objects are equal
289      */
290     @Override
291     public boolean equals(Object obj)
292     {
293         if (this == obj)
294         {
295             return true;
296         }
297         if (!(obj instanceof PercentData))
298         {
299             return false;
300         }
301 
302         PercentData c = (PercentData) obj;
303         return getColumn() == c.getColumn()
304                 && getRow() == c.getRow()
305                 && getSpanX() == c.getSpanX()
306                 && getSpanY() == c.getSpanY()
307                 && getTargetColumn() == c.getTargetColumn()
308                 && getTargetRow() == c.getTargetRow()
309                 && ObjectUtils.equals(getColumnConstraints(), c
310                         .getColumnConstraints())
311                 && ObjectUtils.equals(getRowConstraints(), c
312                         .getRowConstraints());
313     }
314 
315     /**
316      * Returns a hash code for this object.
317      *
318      * @return a hash code
319      */
320     @Override
321     public int hashCode()
322     {
323         final int factor = 31;
324         final int seed = 17;
325 
326         int result = seed;
327         result = factor * result + getColumn();
328         result = factor * result + getRow();
329         result = factor * result + getSpanX();
330         result = factor * result + getSpanY();
331         result = factor * result + getTargetColumn();
332         result = factor * result + getTargetRow();
333         if (getColumnConstraints() != null)
334         {
335             result = factor * result + getColumnConstraints().hashCode();
336         }
337         if (getRowConstraints() != null)
338         {
339             result = factor * result + getRowConstraints().hashCode();
340         }
341 
342         return result;
343     }
344 
345     /**
346      * <p>
347      * A <em>builder</em> implementation for creating instances of {@code
348      * PercentData}. Using this class {@code PercentData} objects with all
349      * properties supported can be created in a convenient way. Refer to the
350      * class comment for {@link PercentData} for example use cases for this
351      * class.
352      * </p>
353      * <p>
354      * Implementation note: This class is not thread-safe.
355      * </p>
356      *
357      * @author Oliver Heger
358      * @version $Id: PercentData.java 205 2012-01-29 18:29:57Z oheger $
359      */
360     public static class Builder implements Serializable
361     {
362         /**
363          * The serial version UID.
364          */
365         private static final long serialVersionUID = 20090730L;
366 
367         /** The x position. */
368         private int col;
369 
370         /** The y position. */
371         private int row;
372 
373         /** The x span. */
374         private int spanX;
375 
376         /** The y span. */
377         private int spanY;
378 
379         /** The target column. */
380         private int targetCol;
381 
382         /** The target row. */
383         private int targetRow;
384 
385         /** The column constraints. */
386         private CellConstraints ccCol;
387 
388         /** The row constraints. */
389         private CellConstraints ccRow;
390 
391         /**
392          * Creates a new instance of {@code Builder}.
393          */
394         public Builder()
395         {
396             initDefaults();
397         }
398 
399         /**
400          * Initializes the column and the row index of the {@code PercentData}
401          * instance to be created.
402          *
403          * @param x the column index (must be greater or equal 0)
404          * @param y the row index (must be greater or equal 0)
405          * @return a reference to this builder for method chaining
406          * @throws IllegalArgumentException if a parameter is invalid
407          */
408         public Builder xy(int x, int y)
409         {
410             checkPos(x, "Column");
411             checkPos(y, "Row");
412 
413             col = x;
414             row = y;
415             return this;
416         }
417 
418         /**
419          * Initializes the number of columns occupied by the component
420          * associated with the {@code PercentData} instance to be created. This
421          * value is used to populate the {@code spanX} property of the {@code
422          * PercentData} object.
423          *
424          * @param w the number of occupied columns (must be greater 0)
425          * @return a reference to this builder for method chaining
426          * @throws IllegalArgumentException if the number of columns is less or
427          *         equal 0
428          */
429         public Builder spanX(int w)
430         {
431             if (w < 1)
432             {
433                 throw new IllegalArgumentException("X span must be greater 0!");
434             }
435 
436             spanX = w;
437             return this;
438         }
439 
440         /**
441          * Initializes the number of rows occupied by the component associated
442          * with the {@code PercentData} instance to be created. This value is
443          * used to populate the {@code spanY} property of the {@code
444          * PercentData} object.
445          *
446          * @param h the number of occupied rows (must be greater 0)
447          * @return a reference to this builder for method chaining
448          * @throws IllegalArgumentException if the number of rows is less or
449          *         equal 0
450          */
451         public Builder spanY(int h)
452         {
453             if (h < 1)
454             {
455                 throw new IllegalArgumentException("Y span must be greater 0!");
456             }
457 
458             spanY = h;
459             return this;
460         }
461 
462         /**
463          * Initializes the number of columns and rows occupied by the component
464          * association with the {@code PercentData} instance to be created. This
465          * is convenience method that combines calls to {@link #spanX(int)} and
466          * {@link #spanY(int)}.
467          *
468          * @param w the number of occupied columns (must be greater 0)
469          * @param h the number of occupied rows (must be greater 0)
470          * @return a reference to this builder for method chaining
471          * @throws IllegalArgumentException if the number of columns or rows is
472          *         less or equal 0
473          */
474         public Builder span(int w, int h)
475         {
476             spanX(w);
477             spanY(h);
478             return this;
479         }
480 
481         /**
482          * Initializes the {@code targetColumn} property of the {@code
483          * PercentData} instance to be created. If a component spans multiple
484          * columns, the target column specifies, to which column the size of the
485          * component should be applied. If no target column is set (which is the
486          * default), the width of the component is not taken into account when
487          * determining the width of the layout's columns.
488          *
489          * @param tc the index of the target column (must be greater or equal 0)
490          * @return a reference to this builder for method chaining
491          * @throws IllegalArgumentException if the target column is less than 0
492          */
493         public Builder withTargetColumn(int tc)
494         {
495             checkPos(tc, "Target column");
496             targetCol = tc;
497             return this;
498         }
499 
500         /**
501          * Initializes the {@code targetRow} property of the {@code PercentData}
502          * instance to be created. If a component spans multiple rows, the
503          * target row specifies, to which row the size of the component should
504          * be applied. If no target row is set (which is the default), the
505          * height of the component is not taken into account when determining
506          * the height of the layout's rows.
507          *
508          * @param tr the index of the target row (must be greater or equal 0)
509          * @return a reference to this builder for method chaining
510          * @throws IllegalArgumentException if the target row is less than 0
511          */
512         public Builder withTargetRow(int tr)
513         {
514             checkPos(tr, "Target row");
515             targetRow = tr;
516             return this;
517         }
518 
519         /**
520          * Sets a {@code CellConstraints} reference for the column for the
521          * {@code PercentData} instance to be created. With this method the
522          * {@code columnConstraints} property of the {@code PercentData} object
523          * can be specified.
524          *
525          * @param cc the {@code CellConstraints} object for the column
526          * @return a reference to this builder for method chaining
527          */
528         public Builder withColumnConstraints(CellConstraints cc)
529         {
530             ccCol = cc;
531             return this;
532         }
533 
534         /**
535          * Sets a {@code CellConstraints} reference for the row for the {@code
536          * PercentData} instance to be created. With this method the {@code
537          * rowConstraints} property of the {@code PercentData} object can be
538          * specified.
539          *
540          * @param cc the {@code CellConstraints} object for the row
541          * @return a reference to this builder for method chaining
542          */
543         public Builder withRowConstraints(CellConstraints cc)
544         {
545             ccRow = cc;
546             return this;
547         }
548 
549         /**
550          * Returns a {@code PercentData} object with the specified column and
551          * row index. This is a convenience method for the frequent use case
552          * that only the position of a component in the layout needs to be
553          * specified. It has the same effect as calling {@link #xy(int, int)}
554          * followed by {@link #create()}. Properties that have been set before
555          * are not cleared.
556          *
557          * @param x the column index (must be greater or equal 0)
558          * @param y the row index (must be greater or equal 0)
559          * @return the {@code PercentData} object with the given coordinates
560          * @throws IllegalArgumentException if a parameter is invalid
561          */
562         public PercentData pos(int x, int y)
563         {
564             xy(x, y);
565             return create();
566         }
567 
568         /**
569          * Creates the {@code PercentData} instance whose properties were
570          * specified by the preceding method calls. This method requires that
571          * the position was set using the {@link #xy(int, int)} method. All
572          * other properties are optional and are initialized with the following
573          * default values:
574          * <ul>
575          * <li>The span is set to (1, 1), i.e. the component covers a single
576          * column and row.</li>
577          * <li>The target column and row are set to -1, which means that they
578          * are undefined.</li>
579          * <li>The column constraints and row constraints are set to
580          * <b>null</b>.</li>
581          * </ul>
582          *
583          * @return a reference to the new {@code PercentData} instance
584          * @throws IllegalStateException if required parameters have not been
585          *         set
586          */
587         public PercentData create()
588         {
589             if (col <= POS_UNDEF)
590             {
591                 throw new IllegalStateException(
592                         "A position must be set before calling create()!");
593             }
594 
595             PercentData res = new PercentData(col, row, spanX, spanY,
596                     targetCol, targetRow, ccCol, ccRow);
597             reset();
598             return res;
599         }
600 
601         /**
602          * Resets all properties of this builder to default values. This method
603          * is automatically called by {@link #create()}, so that the definition
604          * of a new instance can be started. It can be invoked manually to undo
605          * the effects of methods called before.
606          */
607         public void reset()
608         {
609             initDefaults();
610         }
611 
612         /**
613          * Sets default values for the builder properties.
614          */
615         private void initDefaults()
616         {
617             col = POS_UNDEF;
618             row = POS_UNDEF;
619             targetCol = POS_UNDEF;
620             targetRow = POS_UNDEF;
621             spanX = 1;
622             spanY = 1;
623             ccCol = null;
624             ccRow = null;
625         }
626 
627         /**
628          * Helper method for validating a position. This method checks whether
629          * the given position is valid. If not, an exception is thrown.
630          *
631          * @param p the position to check
632          * @param name the name of the position for generating the exception
633          *        message
634          */
635         private static void checkPos(int p, String name)
636         {
637             if (p <= POS_UNDEF)
638             {
639                 throw new IllegalArgumentException(name
640                         + " must be greater or equal 0!");
641             }
642         }
643     }
644 }