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: "
42 * ,;/". Examples for valid strings would be:
43 * </p>
44 * <p>
45 *
46 * <pre>
47 * "1 3 4"
48 * "1, 3,4"
49 * "1;3/4" 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 }