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  import java.util.Arrays;
20  import java.util.StringTokenizer;
21  
22  /**
23   * <p>
24   * A simple helper class to define columns and rows with identical size in
25   * {@link PercentLayout}.
26   * </p>
27   * <p>
28   * In GUI design it is often desirable that certain columns and rows have
29   * exactly the same size, independent from the components they contain. An
30   * example would be columns with labels that have a different width. With this
31   * class it is possible to define the indices of columns and rows that should
32   * have the same size.
33   * </p>
34   * <p>
35   * This class defines some convenience constructors for dealing with a limited
36   * number of cells. There is also a generic constructor which expects an array
37   * with cell indices. The numeric values passed to the constructors are
38   * interpreted as 0-based indices of columns and rows that have already been
39   * defined in <code>PercentLayout</code>. It is also possible to set the indices
40   * of the affected cells using a string format. Valid strings simply contain the
41   * numeric indices separated by one or more of the following characters: &quot;
42   * ,;/&quot;. Examples for valid strings would be:
43   * </p>
44   * <p>
45   *
46   * <pre>
47   * &quot;1 3 4&quot;
48   * &quot;1, 3,4&quot;
49   * &quot;1;3/4&quot;  etc.
50   * </pre>
51   *
52   * </p>
53   * <p>
54   * <code>CellGroup</code> objects are immutable. There are no setter methods for
55   * manipulating instances once they have been created.
56   * </p>
57   *
58   * @author Oliver Heger
59   * @version $Id: CellGroup.java 205 2012-01-29 18:29:57Z oheger $
60   */
61  public class CellGroup implements Serializable
62  {
63      /**
64       * The serial version UID.
65       */
66      private static final long serialVersionUID = 20090730L;
67  
68      /** Constant for the supported separator characters. */
69      private static final String SEPARATORS = " ,;/";
70  
71      /** Stores the indices of the affected cells. */
72      private final int[] indices;
73  
74      /**
75       * Creates a new instance of {@code CellGroup} and initializes it with the
76       * given array with indices. This constructor also performs some checks on
77       * the array passed in.
78       *
79       * @param idxarray the array with the indices (must not be <b>null</b>)
80       * @throws IllegalArgumentException if the array contains invalid indices
81       */
82      private CellGroup(int[] idxarray)
83      {
84          if (idxarray.length <= 1)
85          {
86              throw new IllegalArgumentException(
87                      "Group must contain at least 2 indices!");
88          }
89  
90          indices = idxarray.clone();
91          Arrays.sort(indices);
92          if (indices[0] < 0)
93          {
94              throw new IllegalArgumentException("Invalid index: " + indices[0]);
95          }
96      }
97  
98      /**
99       * Creates a new instance of <code>CellGroup</code>. The group contains the
100      * two passed in cells.
101      *
102      * @param idx1 the index of the first cell
103      * @param idx2 the index of the second cell
104      */
105     public CellGroup(int idx1, int idx2)
106     {
107         this(new int[] {
108                 idx1, idx2
109         });
110     }
111 
112     /**
113      * Creates a new instance of <code>CellGroup</code>. The group contains the
114      * three passed in cells.
115      *
116      * @param idx1 the index of the first cell
117      * @param idx2 the index of the second cell
118      * @param idx3 the index of the third cell
119      */
120     public CellGroup(int idx1, int idx2, int idx3)
121     {
122         this(new int[] {
123                 idx1, idx2, idx3
124         });
125     }
126 
127     /**
128      * Creates a new instance of <code>CellGroup</code>. The group contains the
129      * four passed in cells.
130      *
131      * @param idx1 the index of the first cell
132      * @param idx2 the index of the second cell
133      * @param idx3 the index of the third cell
134      * @param idx4 the index of the fourth cell
135      */
136     public CellGroup(int idx1, int idx2, int idx3, int idx4)
137     {
138         this(new int[] {
139                 idx1, idx2, idx3, idx4
140         });
141     }
142 
143     /**
144      * Creates a new instance of <code>CellGroup</code> and initializes it with
145      * the indices of the affected cells.
146      *
147      * @param idx an array with the cell indices
148      * @return the newly created {@code CellGroup} object
149      */
150     public static CellGroup fromArray(int... idx)
151     {
152         if (idx == null)
153         {
154             throw new IllegalArgumentException("Index array must not be null!");
155         }
156 
157         return new CellGroup(idx);
158     }
159 
160     /**
161      * Creates a new instance of <code>CellGroup</code> and initializes it from
162      * the passed in string.
163      *
164      * @param s a string defining the cell indices
165      * @return the newly created {@code CellGroup} object
166      */
167     public static CellGroup fromString(String s)
168     {
169         return new CellGroup(parse(s));
170     }
171 
172     /**
173      * Returns the number of elements contained in this group.
174      *
175      * @return the number of elements
176      */
177     public int groupSize()
178     {
179         return indices.length;
180     }
181 
182     /**
183      * Applies this group object to the given cell sizes. This method checks the
184      * sizes of all cells that belong to this group and sets them to the
185      * maximum.
186      *
187      * @param sizes an array with the cell sizes (must not be <b>null</b>)
188      * @throws IllegalArgumentException if the array with sizes is <b>null</b>
189      * @throws ArrayIndexOutOfBoundsException if indices in this group are too
190      *         big
191      */
192     public void apply(int[] sizes)
193     {
194         if (sizes == null)
195         {
196             throw new IllegalArgumentException("Sizes array must not be null!");
197         }
198 
199         // Calculate maximum size
200         int max = 0;
201         for (int i = 0; i < indices.length; i++)
202         {
203             if (sizes[indices[i]] > max)
204             {
205                 max = sizes[indices[i]];
206             }
207         }
208 
209         // Apply this maximum size
210         for (int i = 0; i < indices.length; i++)
211         {
212             sizes[indices[i]] = max;
213         }
214     }
215 
216     /**
217      * Appends a string representation of this object to the given string
218      * buffer. This string contains only the indices of this group separated by
219      * ",".
220      *
221      * @param buf the target buffer (must not be <b>null</b>)
222      * @throws IllegalArgumentException if the target buffer is <b>null</b>
223      */
224     public void buildString(StringBuilder buf)
225     {
226         if (buf == null)
227         {
228             throw new IllegalArgumentException("Buffer must not be null!");
229         }
230 
231         buf.append(Arrays.toString(indices));
232     }
233 
234     /**
235      * Creates a string representation of this object.
236      *
237      * @return a string representation of this object
238      */
239     @Override
240     public String toString()
241     {
242         StringBuilder buf = new StringBuilder();
243         buf.append("CellGroup [ indices = ");
244         buildString(buf);
245         buf.append(" ]");
246         return buf.toString();
247     }
248 
249     /**
250      * Compares this object with another one. Two instances of {@code CellGroup}
251      * are equal if they contain the same indices (the order does not matter).
252      *
253      * @param obj the object to compare to
254      * @return a flag whether these objects are equal
255      */
256     @Override
257     public boolean equals(Object obj)
258     {
259         if (this == obj)
260         {
261             return true;
262         }
263         if (!(obj instanceof CellGroup))
264         {
265             return false;
266         }
267 
268         CellGroup c = (CellGroup) obj;
269         return Arrays.equals(indices, c.indices);
270     }
271 
272     /**
273      * Returns a hash code for this object.
274      *
275      * @return a hash code
276      */
277     @Override
278     public int hashCode()
279     {
280         return Arrays.hashCode(indices);
281     }
282 
283     /**
284      * Parses the specified string, which must contain cell indices. Refer to
285      * the class comment for a description of the expected format. If the string
286      * is invalid, an <code>IllegalArgumentException</code> exception is thrown.
287      *
288      * @param s the string
289      * @return an array with the indices extracted from the string
290      * @throws IllegalArgumentException if the string is invalid
291      */
292     private static int[] parse(String s)
293     {
294         if (s == null)
295         {
296             throw new IllegalArgumentException("String must not be null!");
297         }
298 
299         StringTokenizer tok = new StringTokenizer(s, SEPARATORS);
300         int[] indices = new int[tok.countTokens()];
301 
302         try
303         {
304             for (int i = 0; i < indices.length; i++)
305             {
306                 indices[i] = Integer.parseInt(tok.nextToken());
307             }
308         }
309         catch (NumberFormatException nex)
310         {
311             throw new IllegalArgumentException(
312                     "Invalid specification string for CellGroup: " + s);
313         }
314 
315         return indices;
316     }
317 }