1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of CosyBeans-Common.
5 *
6 * CosyBeans-Common is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CosyBeans-Common is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CosyBeans-Common. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.gui.components.ledder;
21
22 import java.awt.Color;
23
24 import java.beans.PropertyChangeListener;
25 import java.beans.PropertyChangeSupport;
26
27 import java.io.Serializable;
28
29
30 /**
31 * This is generic class providing access to the <code>BitDescriptor</code>. In
32 * most cases this implementation should provide all neccessary access to the
33 * model. This class is designed for convenient access and minimal overhead.
34 *
35 * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
36 * @version $id$
37 */
38 public class BitmaskField implements Serializable
39 {
40 // Model holding individual led information.
41 private BitDescriptor bitDescriptor;
42
43 // Value holding the mask value
44 private long mask = 0;
45
46 // Value holding the bit pattern.
47 private long bits = 0;
48
49 // When used as mask, the mask will be ignored completely.
50
51 /** DOCUMENT ME! */
52 public static final int IGNORE_MASK = -1;
53
54 // Cache variable used when calling getBitCount to prevent unnecessary mask
55 // parsing. Negative value indicates that mask will be parsed on next call,
56 // nonnegative indicates valid bit count.
57 private transient int lastBitCount = -1;
58
59 // Support class to handle firing of property change events.
60 private final PropertyChangeSupport changes = new PropertyChangeSupport(this);
61
62 /**
63 * Default constructor for BitmaskField.
64 */
65 public BitmaskField()
66 {
67 this(null);
68 }
69
70 /**
71 * Constructor for BitmaskField that also initialized the values to the
72 * specified model.
73 *
74 * @param ledModel BitDescriptor Initial model to use.
75 */
76 public BitmaskField(BitDescriptor ledModel)
77 {
78 super();
79 setBitDescriptor(ledModel);
80 }
81
82 /**
83 * Creates default model. Override this method to create a different
84 * implementation of BitDescriptor by default. This method will be called
85 * whenever it is neccessary to create a new model ensuring the model
86 * always exists.
87 *
88 * @return BitDescriptor
89 */
90 protected BitDescriptor createDefaultModel()
91 {
92 return new DefaultBitDescriptor();
93 }
94
95 /**
96 * Returns current model used to represent the bit values.
97 *
98 * @return BitDescriptor
99 */
100 public BitDescriptor getBitDescriptor()
101 {
102 if (bitDescriptor == null) {
103 bitDescriptor = new DefaultBitDescriptor();
104 }
105
106 return bitDescriptor;
107 }
108
109 /**
110 * DOCUMENT ME!
111 *
112 * @return
113 */
114 private int size()
115 {
116 long tmpMask = getMask();
117 int size = 0;
118 boolean done = false;
119
120 while (!done) {
121 if (tmpMask % 2 == 1) {
122 size++;
123 }
124
125 tmpMask = tmpMask >> 1;
126
127 if (tmpMask == 0) {
128 done = true;
129 }
130 }
131
132 return size;
133 }
134
135 /**
136 * Sets the model to represent bit values. If the new model is null,
137 * default model will be used as created by
138 * <code>createDefaultModel()</code> method. Also, setting the same
139 * instance of the model multiple times will not cause any change to
140 * occur. If the new model is null, a new instance of model will be
141 * created using <code>createDefaultModel()</code> method.
142 *
143 * @param newModel BitDescriptor
144 */
145 public void setBitDescriptor(BitDescriptor newModel)
146 {
147 if (bitDescriptor == newModel) {
148 return;
149 }
150
151 BitDescriptor oldValue = bitDescriptor;
152
153 // Remove the listener from the old model.
154 if (oldValue != null) {
155 //TODO oldValue.removeLedModelListener(ledModelListener);
156 }
157
158 bitDescriptor = newModel;
159
160 // Ensure the new model is not null.
161 if (bitDescriptor == null) {
162 bitDescriptor = createDefaultModel();
163 }
164
165 // Register listener on the new model.
166 //TODO bitDescriptor.addLedModelListener(ledModelListener);
167 changes.firePropertyChange("model", oldValue, newModel);
168 }
169
170 /**
171 * Returns number of bits defined by mask. Counts each bit in mask and
172 * increases the counter for each value of 1.
173 *
174 * <p>
175 * If the mask is defined as 0010110111, getBitCount returns 6.
176 * </p>
177 *
178 * <p>
179 * Always returns non-negative integer.
180 * </p>
181 *
182 * <p>
183 * This method may be used to discover the number of leds to display. If
184 * the mask is not defined, the return value will be the number of leds in
185 * the model. Value of leds is cached to avoid calculation each time the
186 * method is called and will be changed whenever the model changes.
187 * </p>
188 *
189 * @return int Number of leds
190 */
191 public int getBitCount()
192 {
193 // if (lastBitCount >= 0) {
194 // return lastBitCount;
195 // }
196 if (mask == IGNORE_MASK) {
197 // We ignore the mask and return number of elements in the model.
198 lastBitCount = size();
199 } else {
200 synchronized (this) {
201 long m = mask;
202
203 // Scan the mask bitwise and increment the counter for each bit
204 // value of 1 encountered.
205 while (m > 0) {
206 lastBitCount += m & 1;
207 m >>= 1;
208 }
209
210 lastBitCount = size();
211 }
212 }
213
214 return lastBitCount;
215 }
216
217 /**
218 * Resets the cache for the <code>getBitCount()</code> method. This should
219 * be called whenever the number of leds changes.
220 */
221 protected final void flushBitCount()
222 {
223 lastBitCount = -1;
224 }
225
226 /**
227 * Sets the new bit pattern. To avoid unneccesary updates, the property
228 * change event will only be fired if the actual value has changed.
229 *
230 * @param value long New bit pattern.
231 */
232 public void setBits(long value)
233 {
234 if (bits == value) {
235 return;
236 }
237
238 long oldValue = bits;
239
240 bits = value;
241
242 firePropertyChange("bits", oldValue, value);
243 }
244
245 /**
246 * Returns the bit pattern currently set.
247 *
248 * @return long Bit pattern.
249 */
250 public long getBits()
251 {
252 return bits;
253 }
254
255 /**
256 * Sets the mask to use. This may be bitwise specification of which bits in
257 * the bit pattern are valid or IGNORE_MASK constant. In the latter case
258 * mask parameter will be ignored entirely.
259 *
260 * @param value long New mask.
261 */
262 public void setMask(long value)
263 {
264 if (mask == value) {
265 return;
266 }
267
268 long oldValue = mask;
269
270 mask = value;
271
272 flushBitCount();
273
274 firePropertyChange("mask", oldValue, value);
275 }
276
277 /**
278 * Returns the mask. The return value will be bitwise pattern indicating
279 * active bits or IGNORE_MASK constant.
280 *
281 * @return long Current mask.
282 */
283 public long getMask()
284 {
285 return mask;
286 }
287
288 /**
289 * Returns array containing descriptions for the leds. This is a
290 * convenience method. To avoid creation of new String array, query the
291 * BitDescriptor directly.
292 *
293 * @return String[] Descriptions of the leds.
294 */
295 public String[] getDescriptions()
296 {
297 synchronized (this) {
298 BitDescriptor lm = getBitDescriptor();
299
300 final String[] descriptions = new String[size()];
301
302 for (int i = 0; i < size(); i++) {
303 descriptions[i] = lm.getDescription(i);
304 }
305
306 return descriptions;
307 }
308 }
309
310 /**
311 * Returns array of colors to be used for rendering the individual led's ON
312 * state. This is a convenience method. To avoid creation of new Color
313 * array, query the BitDescriptor directly.
314 *
315 * @return Color[] Colors indicating the ON state.
316 */
317 public Color[] getColorsWhenOn()
318 {
319 synchronized (this) {
320 BitDescriptor lm = getBitDescriptor();
321
322 Color[] colors = new Color[size()];
323
324 for (int i = 0; i < size(); i++) {
325 colors[i] = lm.getColorWhenOn(i);
326 }
327
328 return colors;
329 }
330 }
331
332 /**
333 * Returns array of colors to be used for rendering the individual led's
334 * OFF state. This is a convenience method. To avoid creation of new Color
335 * array, query the BitDescriptor directly.
336 *
337 * @return Color[] Colors indicating the OFF state.
338 */
339 public Color[] getColorsWhenOff()
340 {
341 synchronized (this) {
342 BitDescriptor lm = getBitDescriptor();
343
344 Color[] colors = new Color[size()];
345
346 for (int i = 0; i < size(); i++) {
347 colors[i] = lm.getColorWhenOff(i);
348 }
349
350 return colors;
351 }
352 }
353
354 /**
355 * Returns array of colors corresponding to current state of bit pattern.
356 * This is a convenience method. To avoid creation of new Color array,
357 * query the BitDescriptor directly.
358 *
359 * @return Color[]
360 */
361 public Color[] toColor()
362 {
363 int i = 0;
364 long m = mask;
365 long v = bits;
366
367 int n = getBitCount();
368 final Color[] colors = new Color[n];
369
370 synchronized (this) {
371 final BitDescriptor lm = getBitDescriptor();
372
373 while (m > 0) {
374 if ((m & 1) == 1) {
375 colors[i++] = ((v & 1) == 1) ? lm.getColorWhenOn(i)
376 : lm.getColorWhenOff(i);
377 }
378
379 m >>= 1;
380 v >>= 1;
381 }
382 }
383
384 return colors;
385 }
386
387 /**
388 * Returns bit pattern by ignoring all zeros in the mask. If mask is set to
389 * IGNORE_MASK, return value is equal to bits parameter.
390 *
391 * <p>
392 * Example: <br>
393 * <code> mask = XX10101 bits = XX10010 getCompactValue() = XXXXX100
394 * </code>
395 * </p>
396 *
397 * @return long Compact representation of the bit pattern.
398 */
399 public long getCompactValue()
400 {
401 if (mask == IGNORE_MASK) {
402 return bits;
403 }
404
405 long m = mask;
406 long v = bits;
407 long vi = 0;
408 long newBits = 0;
409
410 while (m > 0) {
411 if ((m & 1) == 1) {
412 newBits += ((v & 1) << vi++);
413 }
414
415 m >>= 1;
416 v >>= 1;
417 }
418
419 return newBits;
420 }
421
422 /**
423 * Convenience method that fires property change event with long
424 * parameters. To avoid unneccesary creation of <code>Long</code> objects
425 * the event is only fired if any listeners have been registered.
426 *
427 * @param property String
428 * @param old long
429 * @param now long
430 */
431 protected void firePropertyChange(String property, long old, long now)
432 {
433 if (changes.hasListeners(property)) {
434 changes.firePropertyChange(property, new Long(old), new Long(now));
435 }
436 }
437
438 /**
439 * Adds property change listener.
440 *
441 * @param listener PropertyChangeListener
442 */
443 public void addPropertyChangeListener(PropertyChangeListener listener)
444 {
445 changes.addPropertyChangeListener(listener);
446 }
447
448 /**
449 * Removes property change listener.
450 *
451 * @param listener PropertyChangeListener
452 */
453 public void removePropertyChangeListener(PropertyChangeListener listener)
454 {
455 changes.removePropertyChangeListener(listener);
456 }
457 }
458
459 /* __oOo__ */