View Javadoc

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__ */