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