1 /*
   2  * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.swing.plaf.synth;
  26 
  27 import javax.swing.plaf.synth.*;
  28 import java.awt.*;
  29 import java.util.*;
  30 import javax.swing.*;
  31 import javax.swing.border.Border;
  32 import javax.swing.plaf.*;
  33 
  34 /**
  35  * Default implementation of SynthStyle. Has setters for the various
  36  * SynthStyle methods. Many of the properties can be specified for all states,
  37  * using SynthStyle directly, or a specific state using one of the StateInfo
  38  * methods.
  39  * <p>
  40  * Beyond the constructor a subclass should override the <code>addTo</code>
  41  * and <code>clone</code> methods, these are used when the Styles are being
  42  * merged into a resulting style.
  43  *
  44  * @author Scott Violet
  45  */
  46 public class DefaultSynthStyle extends SynthStyle implements Cloneable {
  47     private static final String PENDING = "Pending";
  48 
  49     /**
  50      * Should the component be opaque?
  51      */
  52     private boolean opaque;
  53     /**
  54      * Insets.
  55      */
  56     private Insets insets;
  57     /**
  58      * Information specific to ComponentState.
  59      */
  60     private StateInfo[] states;
  61     /**
  62      * User specific data.
  63      */
  64     private Map data;
  65 
  66     /**
  67      * Font to use if there is no matching StateInfo, or the StateInfo doesn't
  68      * define one.
  69      */
  70     private Font font;
  71 
  72     /**
  73      * SynthGraphics, may be null.
  74      */
  75     private SynthGraphicsUtils synthGraphics;
  76 
  77     /**
  78      * Painter to use if the StateInfo doesn't have one.
  79      */
  80     private SynthPainter painter;
  81 
  82 
  83     /**
  84      * Nullary constructor, intended for subclassers.
  85      */
  86     public DefaultSynthStyle() {
  87     }
  88 
  89     /**
  90      * Creates a new DefaultSynthStyle that is a copy of the passed in
  91      * style. Any StateInfo's of the passed in style are clonsed as well.
  92      *
  93      * @param style Style to duplicate
  94      */
  95     public DefaultSynthStyle(DefaultSynthStyle style) {
  96         opaque = style.opaque;
  97         if (style.insets != null) {
  98             insets = new Insets(style.insets.top, style.insets.left,
  99                                 style.insets.bottom, style.insets.right);
 100         }
 101         if (style.states != null) {
 102             states = new StateInfo[style.states.length];
 103             for (int counter = style.states.length - 1; counter >= 0;
 104                      counter--) {
 105                 states[counter] = (StateInfo)style.states[counter].clone();
 106             }
 107         }
 108         if (style.data != null) {
 109             data = new HashMap();
 110             data.putAll(style.data);
 111         }
 112         font = style.font;
 113         synthGraphics = style.synthGraphics;
 114         painter = style.painter;
 115     }
 116 
 117     /**
 118      * Creates a new DefaultSynthStyle.
 119      *
 120      * @param insets Insets for the Style
 121      * @param opaque Whether or not the background is completely painted in
 122      *        an opaque color
 123      * @param states StateInfos describing properties per state
 124      * @param data Style specific data.
 125      */
 126     public DefaultSynthStyle(Insets insets, boolean opaque,
 127                              StateInfo[] states, Map data) {
 128         this.insets = insets;
 129         this.opaque = opaque;
 130         this.states = states;
 131         this.data = data;
 132     }
 133 
 134     public Color getColor(SynthContext context, ColorType type) {
 135         return getColor(context.getComponent(), context.getRegion(),
 136                         context.getComponentState(), type);
 137     }
 138 
 139     public Color getColor(JComponent c, Region id, int state,
 140                           ColorType type) {
 141         // For the enabled state, prefer the widget's colors
 142         if (!id.isSubregion() && state == SynthConstants.ENABLED) {
 143             if (type == ColorType.BACKGROUND) {
 144                 return c.getBackground();
 145             }
 146             else if (type == ColorType.FOREGROUND) {
 147                 return c.getForeground();
 148             }
 149             else if (type == ColorType.TEXT_FOREGROUND) {
 150                 // If getForeground returns a non-UIResource it means the
 151                 // developer has explicitly set the foreground, use it over
 152                 // that of TEXT_FOREGROUND as that is typically the expected
 153                 // behavior.
 154                 Color color = c.getForeground();
 155                 if (!(color instanceof UIResource)) {
 156                     return color;
 157                 }
 158             }
 159         }
 160         // Then use what we've locally defined
 161         Color color = getColorForState(c, id, state, type);
 162         if (color == null) {
 163             // No color, fallback to that of the widget.
 164             if (type == ColorType.BACKGROUND ||
 165                         type == ColorType.TEXT_BACKGROUND) {
 166                 return c.getBackground();
 167             }
 168             else if (type == ColorType.FOREGROUND ||
 169                      type == ColorType.TEXT_FOREGROUND) {
 170                 return c.getForeground();
 171             }
 172         }
 173         return color;
 174     }
 175 
 176     protected Color getColorForState(SynthContext context, ColorType type) {
 177         return getColorForState(context.getComponent(), context.getRegion(),
 178                                 context.getComponentState(), type);
 179     }
 180 
 181     /**
 182      * Returns the color for the specified state.
 183      *
 184      * @param c JComponent the style is associated with
 185      * @param id Region identifier
 186      * @param state State of the region.
 187      * @param type Type of color being requested.
 188      * @return Color to render with
 189      */
 190     protected Color getColorForState(JComponent c, Region id, int state,
 191                                      ColorType type) {
 192         // Use the best state.
 193         StateInfo si = getStateInfo(state);
 194         Color color;
 195         if (si != null && (color = si.getColor(type)) != null) {
 196             return color;
 197         }
 198         if (si == null || si.getComponentState() != 0) {
 199             si = getStateInfo(0);
 200             if (si != null) {
 201                 return si.getColor(type);
 202             }
 203         }
 204         return null;
 205     }
 206 
 207     /**
 208      * Sets the font that is used if there is no matching StateInfo, or
 209      * it does not define a font.
 210      *
 211      * @param font Font to use for rendering
 212      */
 213     public void setFont(Font font) {
 214         this.font = font;
 215     }
 216 
 217     public Font getFont(SynthContext state) {
 218         return getFont(state.getComponent(), state.getRegion(),
 219                        state.getComponentState());
 220     }
 221 
 222     public Font getFont(JComponent c, Region id, int state) {
 223         if (!id.isSubregion() && state == SynthConstants.ENABLED) {
 224             return c.getFont();
 225         }
 226         Font cFont = c.getFont();
 227         if (cFont != null && !(cFont instanceof UIResource)) {
 228             return cFont;
 229         }
 230         return getFontForState(c, id, state);
 231     }
 232 
 233     /**
 234      * Returns the font for the specified state. This should NOT callback
 235      * to the JComponent.
 236      *
 237      * @param c JComponent the style is associated with
 238      * @param id Region identifier
 239      * @param state State of the region.
 240      * @return Font to render with
 241      */
 242     protected Font getFontForState(JComponent c, Region id, int state) {
 243         if (c == null) {
 244             return this.font;
 245         }
 246         // First pass, look for the best match
 247         StateInfo si = getStateInfo(state);
 248         Font font;
 249         if (si != null && (font = si.getFont()) != null) {
 250             return font;
 251         }
 252         if (si == null || si.getComponentState() != 0) {
 253             si = getStateInfo(0);
 254             if (si != null && (font = si.getFont()) != null) {
 255                 return font;
 256             }
 257         }
 258         // Fallback font.
 259         return this.font;
 260     }
 261 
 262     protected Font getFontForState(SynthContext context) {
 263         return getFontForState(context.getComponent(), context.getRegion(),
 264                                context.getComponentState());
 265     }
 266 
 267     /**
 268      * Sets the SynthGraphicsUtils that will be used for rendering.
 269      *
 270      * @param graphics SynthGraphics
 271      */
 272     public void setGraphicsUtils(SynthGraphicsUtils graphics) {
 273         this.synthGraphics = graphics;
 274     }
 275 
 276     /**
 277      * Returns a SynthGraphicsUtils.
 278      *
 279      * @param context SynthContext indentifying requestor
 280      * @return SynthGraphicsUtils
 281      */
 282     public SynthGraphicsUtils getGraphicsUtils(SynthContext context) {
 283         if (synthGraphics == null) {
 284             return super.getGraphicsUtils(context);
 285         }
 286         return synthGraphics;
 287     }
 288 
 289     /**
 290      * Sets the insets.
 291      *
 292      * @param Insets.
 293      */
 294     public void setInsets(Insets insets) {
 295         this.insets = insets;
 296     }
 297 
 298     /**
 299      * Returns the Insets. If <code>to</code> is non-null the resulting
 300      * insets will be placed in it, otherwise a new Insets object will be
 301      * created and returned.
 302      *
 303      * @param context SynthContext indentifying requestor
 304      * @param to Where to place Insets
 305      * @return Insets.
 306      */
 307     public Insets getInsets(SynthContext state, Insets to) {
 308         if (to == null) {
 309             to = new Insets(0, 0, 0, 0);
 310         }
 311         if (insets != null) {
 312             to.left = insets.left;
 313             to.right = insets.right;
 314             to.top = insets.top;
 315             to.bottom = insets.bottom;
 316         }
 317         else {
 318             to.left = to.right = to.top = to.bottom = 0;
 319         }
 320         return to;
 321     }
 322 
 323     /**
 324      * Sets the Painter to use for the border.
 325      *
 326      * @param painter Painter for the Border.
 327      */
 328     public void setPainter(SynthPainter painter) {
 329         this.painter = painter;
 330     }
 331 
 332     /**
 333      * Returns the Painter for the passed in Component. This may return null.
 334      *
 335      * @param ss SynthContext indentifying requestor
 336      * @return Painter for the border
 337      */
 338     public SynthPainter getPainter(SynthContext ss) {
 339         return painter;
 340     }
 341 
 342     /**
 343      * Sets whether or not the JComponent should be opaque.
 344      *
 345      * @param opaque Whether or not the JComponent should be opaque.
 346      */
 347     public void setOpaque(boolean opaque) {
 348         this.opaque = opaque;
 349     }
 350 
 351     /**
 352      * Returns the value to initialize the opacity property of the Component
 353      * to. A Style should NOT assume the opacity will remain this value, the
 354      * developer may reset it or override it.
 355      *
 356      * @param ss SynthContext indentifying requestor
 357      * @return opaque Whether or not the JComponent is opaque.
 358      */
 359     public boolean isOpaque(SynthContext ss) {
 360         return opaque;
 361     }
 362 
 363     /**
 364      * Sets style specific values. This does NOT copy the data, it
 365      * assigns it directly to this Style.
 366      *
 367      * @param data Style specific values
 368      */
 369     public void setData(Map data) {
 370         this.data = data;
 371     }
 372 
 373     /**
 374      * Returns the style specific data.
 375      *
 376      * @return Style specific data.
 377      */
 378     public Map getData() {
 379         return data;
 380     }
 381 
 382     /**
 383      * Getter for a region specific style property.
 384      *
 385      * @param state SynthContext indentifying requestor
 386      * @param key Property being requested.
 387      * @return Value of the named property
 388      */
 389     public Object get(SynthContext state, Object key) {
 390         // Look for the best match
 391         StateInfo si = getStateInfo(state.getComponentState());
 392         if (si != null && si.getData() != null && getKeyFromData(si.getData(), key) != null) {
 393             return getKeyFromData(si.getData(), key);
 394         }
 395         si = getStateInfo(0);
 396         if (si != null && si.getData() != null && getKeyFromData(si.getData(), key) != null) {
 397             return getKeyFromData(si.getData(), key);
 398         }
 399         if(getKeyFromData(data, key) != null)
 400           return getKeyFromData(data, key);
 401         return getDefaultValue(state, key);
 402     }
 403 
 404 
 405     private Object getKeyFromData(Map stateData, Object key) {
 406           Object value = null;
 407           if (stateData != null) {
 408 
 409             synchronized(stateData) {
 410                 value = stateData.get(key);
 411             }
 412             while (value == PENDING) {
 413                 synchronized(stateData) {
 414                     try {
 415                         stateData.wait();
 416                     } catch (InterruptedException ie) {}
 417                     value = stateData.get(key);
 418                 }
 419             }
 420             if (value instanceof UIDefaults.LazyValue) {
 421                 synchronized(stateData) {
 422                     stateData.put(key, PENDING);
 423                 }
 424                 value = ((UIDefaults.LazyValue)value).createValue(null);
 425                 synchronized(stateData) {
 426                     stateData.put(key, value);
 427                     stateData.notifyAll();
 428                 }
 429             }
 430         }
 431         return value;
 432     }
 433 
 434     /**
 435      * Returns the default value for a particular property.  This is only
 436      * invoked if this style doesn't define a property for <code>key</code>.
 437      *
 438      * @param state SynthContext indentifying requestor
 439      * @param key Property being requested.
 440      * @return Value of the named property
 441      */
 442     public Object getDefaultValue(SynthContext context, Object key) {
 443         return super.get(context, key);
 444     }
 445 
 446     /**
 447      * Creates a clone of this style.
 448      *
 449      * @return Clone of this style
 450      */
 451     public Object clone() {
 452         DefaultSynthStyle style;
 453         try {
 454             style = (DefaultSynthStyle)super.clone();
 455         } catch (CloneNotSupportedException cnse) {
 456             return null;
 457         }
 458         if (states != null) {
 459             style.states = new StateInfo[states.length];
 460             for (int counter = states.length - 1; counter >= 0; counter--) {
 461                 style.states[counter] = (StateInfo)states[counter].clone();
 462             }
 463         }
 464         if (data != null) {
 465             style.data = new HashMap();
 466             style.data.putAll(data);
 467         }
 468         return style;
 469     }
 470 
 471     /**
 472      * Merges the contents of this Style with that of the passed in Style,
 473      * returning the resulting merged syle. Properties of this
 474      * <code>DefaultSynthStyle</code> will take precedence over those of the
 475      * passed in <code>DefaultSynthStyle</code>. For example, if this
 476      * style specifics a non-null font, the returned style will have its
 477      * font so to that regardless of the <code>style</code>'s font.
 478      *
 479      * @param style Style to add our styles to
 480      * @return Merged style.
 481      */
 482     public DefaultSynthStyle addTo(DefaultSynthStyle style) {
 483         if (insets != null) {
 484             style.insets = this.insets;
 485         }
 486         if (font != null) {
 487             style.font = this.font;
 488         }
 489         if (painter != null) {
 490             style.painter = this.painter;
 491         }
 492         if (synthGraphics != null) {
 493             style.synthGraphics = this.synthGraphics;
 494         }
 495         style.opaque = opaque;
 496         if (states != null) {
 497             if (style.states == null) {
 498                 style.states = new StateInfo[states.length];
 499                 for (int counter = states.length - 1; counter >= 0; counter--){
 500                     if (states[counter] != null) {
 501                         style.states[counter] = (StateInfo)states[counter].
 502                                                 clone();
 503                     }
 504                 }
 505             }
 506             else {
 507                 // Find the number of new states in unique, merging any
 508                 // matching states as we go. Also, move any merge styles
 509                 // to the end to give them precedence.
 510                 int unique = 0;
 511                 // Number of StateInfos that match.
 512                 int matchCount = 0;
 513                 int maxOStyles = style.states.length;
 514                 for (int thisCounter = states.length - 1; thisCounter >= 0;
 515                          thisCounter--) {
 516                     int state = states[thisCounter].getComponentState();
 517                     boolean found = false;
 518 
 519                     for (int oCounter = maxOStyles - 1 - matchCount;
 520                              oCounter >= 0; oCounter--) {
 521                         if (state == style.states[oCounter].
 522                                            getComponentState()) {
 523                             style.states[oCounter] = states[thisCounter].
 524                                         addTo(style.states[oCounter]);
 525                             // Move StateInfo to end, giving it precedence.
 526                             StateInfo tmp = style.states[maxOStyles - 1 -
 527                                                          matchCount];
 528                             style.states[maxOStyles - 1 - matchCount] =
 529                                   style.states[oCounter];
 530                             style.states[oCounter] = tmp;
 531                             matchCount++;
 532                             found = true;
 533                             break;
 534                         }
 535                     }
 536                     if (!found) {
 537                         unique++;
 538                     }
 539                 }
 540                 if (unique != 0) {
 541                     // There are states that exist in this Style that
 542                     // don't exist in the other style, recreate the array
 543                     // and add them.
 544                     StateInfo[] newStates = new StateInfo[
 545                                    unique + maxOStyles];
 546                     int newIndex = maxOStyles;
 547 
 548                     System.arraycopy(style.states, 0, newStates, 0,maxOStyles);
 549                     for (int thisCounter = states.length - 1; thisCounter >= 0;
 550                              thisCounter--) {
 551                         int state = states[thisCounter].getComponentState();
 552                         boolean found = false;
 553 
 554                         for (int oCounter = maxOStyles - 1; oCounter >= 0;
 555                                  oCounter--) {
 556                             if (state == style.states[oCounter].
 557                                                getComponentState()) {
 558                                 found = true;
 559                                 break;
 560                             }
 561                         }
 562                         if (!found) {
 563                             newStates[newIndex++] = (StateInfo)states[
 564                                       thisCounter].clone();
 565                         }
 566                     }
 567                     style.states = newStates;
 568                 }
 569             }
 570         }
 571         if (data != null) {
 572             if (style.data == null) {
 573                 style.data = new HashMap();
 574             }
 575             style.data.putAll(data);
 576         }
 577         return style;
 578     }
 579 
 580     /**
 581      * Sets the array of StateInfo's which are used to specify properties
 582      * specific to a particular style.
 583      *
 584      * @param states StateInfos
 585      */
 586     public void setStateInfo(StateInfo[] states) {
 587         this.states = states;
 588     }
 589 
 590     /**
 591      * Returns the array of StateInfo's that that are used to specify
 592      * properties specific to a particular style.
 593      *
 594      * @return Array of StateInfos.
 595      */
 596     public StateInfo[] getStateInfo() {
 597         return states;
 598     }
 599 
 600     /**
 601      * Returns the best matching StateInfo for a particular state.
 602      *
 603      * @param state Component state.
 604      * @return Best matching StateInfo, or null
 605      */
 606     public StateInfo getStateInfo(int state) {
 607         // Use the StateInfo with the most bits that matches that of state.
 608         // If there is none, than fallback to
 609         // the StateInfo with a state of 0, indicating it'll match anything.
 610 
 611         // Consider if we have 3 StateInfos a, b and c with states:
 612         // SELECTED, SELECTED | ENABLED, 0
 613         //
 614         // Input                          Return Value
 615         // -----                          ------------
 616         // SELECTED                       a
 617         // SELECTED | ENABLED             b
 618         // MOUSE_OVER                     c
 619         // SELECTED | ENABLED | FOCUSED   b
 620         // ENABLED                        c
 621 
 622         if (states != null) {
 623             int bestCount = 0;
 624             int bestIndex = -1;
 625             int wildIndex = -1;
 626 
 627             if (state == 0) {
 628                 for (int counter = states.length - 1; counter >= 0;counter--) {
 629                     if (states[counter].getComponentState() == 0) {
 630                         return states[counter];
 631                     }
 632                 }
 633                 return null;
 634             }
 635             for (int counter = states.length - 1; counter >= 0; counter--) {
 636                 int oState = states[counter].getComponentState();
 637 
 638                 if (oState == 0) {
 639                     if (wildIndex == -1) {
 640                         wildIndex = counter;
 641                     }
 642                 }
 643                 else if ((state & oState) == oState) {
 644                     // This is key, we need to make sure all bits of the
 645                     // StateInfo match, otherwise a StateInfo with
 646                     // SELECTED | ENABLED would match ENABLED, which we
 647                     // don't want.
 648 
 649                     // This comes from BigInteger.bitCnt
 650                     int bitCount = oState;
 651                     bitCount -= (0xaaaaaaaa & bitCount) >>> 1;
 652                     bitCount = (bitCount & 0x33333333) + ((bitCount >>> 2) &
 653                                                       0x33333333);
 654                     bitCount = bitCount + (bitCount >>> 4) & 0x0f0f0f0f;
 655                     bitCount += bitCount >>> 8;
 656                     bitCount += bitCount >>> 16;
 657                     bitCount = bitCount & 0xff;
 658                     if (bitCount > bestCount) {
 659                         bestIndex = counter;
 660                         bestCount = bitCount;
 661                     }
 662                 }
 663             }
 664             if (bestIndex != -1) {
 665                 return states[bestIndex];
 666             }
 667             if (wildIndex != -1) {
 668                 return states[wildIndex];
 669             }
 670           }
 671           return null;
 672     }
 673 
 674 
 675     public String toString() {
 676         StringBuffer buf = new StringBuffer();
 677 
 678         buf.append(super.toString()).append(',');
 679 
 680         buf.append("data=").append(data).append(',');
 681 
 682         buf.append("font=").append(font).append(',');
 683 
 684         buf.append("insets=").append(insets).append(',');
 685 
 686         buf.append("synthGraphics=").append(synthGraphics).append(',');
 687 
 688         buf.append("painter=").append(painter).append(',');
 689 
 690         StateInfo[] states = getStateInfo();
 691         if (states != null) {
 692             buf.append("states[");
 693             for (StateInfo state : states) {
 694                 buf.append(state.toString()).append(',');
 695             }
 696             buf.append(']').append(',');
 697         }
 698 
 699         // remove last newline
 700         buf.deleteCharAt(buf.length() - 1);
 701 
 702         return buf.toString();
 703     }
 704 
 705 
 706     /**
 707      * StateInfo represents Style information specific to the state of
 708      * a component.
 709      */
 710     public static class StateInfo {
 711         private Map data;
 712         private Font font;
 713         private Color[] colors;
 714         private int state;
 715 
 716         /**
 717          * Creates a new StateInfo.
 718          */
 719         public StateInfo() {
 720         }
 721 
 722         /**
 723          * Creates a new StateInfo with the specified properties
 724          *
 725          * @param state Component state(s) that this StateInfo should be used
 726          * for
 727          * @param painter Painter responsible for rendering
 728          * @param bgPainter Painter responsible for rendering the background
 729          * @param font Font for this state
 730          * @param colors Colors for this state
 731          */
 732         public StateInfo(int state, Font font, Color[] colors) {
 733             this.state = state;
 734             this.font = font;
 735             this.colors = colors;
 736         }
 737 
 738         /**
 739          * Creates a new StateInfo that is a copy of the passed in
 740          * StateInfo.
 741          *
 742          * @param info StateInfo to copy.
 743          */
 744         public StateInfo(StateInfo info) {
 745             this.state = info.state;
 746             this.font = info.font;
 747             if(info.data != null) {
 748                if(data == null) {
 749                   data = new HashMap();
 750                }
 751                data.putAll(info.data);
 752             }
 753             if (info.colors != null) {
 754                 this.colors = new Color[info.colors.length];
 755                 System.arraycopy(info.colors, 0, colors, 0,info.colors.length);
 756             }
 757         }
 758 
 759         public Map getData() {
 760             return data;
 761         }
 762 
 763         public void setData(Map data) {
 764             this.data = data;
 765         }
 766 
 767         /**
 768          * Sets the font for this state.
 769          *
 770          * @param font Font to use for rendering
 771          */
 772         public void setFont(Font font) {
 773             this.font = font;
 774         }
 775 
 776         /**
 777          * Returns the font for this state.
 778          *
 779          * @return Returns the font to use for rendering this state
 780          */
 781         public Font getFont() {
 782             return font;
 783         }
 784 
 785         /**
 786          * Sets the array of colors to use for rendering this state. This
 787          * is indexed by <code>ColorType.getID()</code>.
 788          *
 789          * @param colors Array of colors
 790          */
 791         public void setColors(Color[] colors) {
 792             this.colors = colors;
 793         }
 794 
 795         /**
 796          * Returns the array of colors to use for rendering this state. This
 797          * is indexed by <code>ColorType.getID()</code>.
 798          *
 799          * @return Array of colors
 800          */
 801         public Color[] getColors() {
 802             return colors;
 803         }
 804 
 805         /**
 806          * Returns the Color to used for the specified ColorType.
 807          *
 808          * @return Color.
 809          */
 810         public Color getColor(ColorType type) {
 811             if (colors != null) {
 812                 int id = type.getID();
 813 
 814                 if (id < colors.length) {
 815                     return colors[id];
 816                 }
 817             }
 818             return null;
 819         }
 820 
 821         /**
 822          * Merges the contents of this StateInfo with that of the passed in
 823          * StateInfo, returning the resulting merged StateInfo. Properties of
 824          * this <code>StateInfo</code> will take precedence over those of the
 825          * passed in <code>StateInfo</code>. For example, if this
 826          * StateInfo specifics a non-null font, the returned StateInfo will
 827          * have its font so to that regardless of the <code>StateInfo</code>'s
 828          * font.
 829          *
 830          * @param info StateInfo to add our styles to
 831          * @return Merged StateInfo.
 832          */
 833         public StateInfo addTo(StateInfo info) {
 834             if (font != null) {
 835                 info.font = font;
 836             }
 837             if(data != null) {
 838                 if(info.data == null) {
 839                     info.data = new HashMap();
 840                 }
 841                 info.data.putAll(data);
 842             }
 843             if (colors != null) {
 844                 if (info.colors == null) {
 845                     info.colors = new Color[colors.length];
 846                     System.arraycopy(colors, 0, info.colors, 0,
 847                                      colors.length);
 848                 }
 849                 else {
 850                     if (info.colors.length < colors.length) {
 851                         Color[] old = info.colors;
 852 
 853                         info.colors = new Color[colors.length];
 854                         System.arraycopy(old, 0, info.colors, 0, old.length);
 855                     }
 856                     for (int counter = colors.length - 1; counter >= 0;
 857                              counter--) {
 858                         if (colors[counter] != null) {
 859                             info.colors[counter] = colors[counter];
 860                         }
 861                     }
 862                 }
 863             }
 864             return info;
 865         }
 866 
 867         /**
 868          * Sets the state this StateInfo corresponds to.
 869          *
 870          * @see SynthConstants
 871          * @param state info.
 872          */
 873         public void setComponentState(int state) {
 874             this.state = state;
 875         }
 876 
 877         /**
 878          * Returns the state this StateInfo corresponds to.
 879          *
 880          * @see SynthConstants
 881          * @return state info.
 882          */
 883         public int getComponentState() {
 884             return state;
 885         }
 886 
 887         /**
 888          * Returns the number of states that are similar between the
 889          * ComponentState this StateInfo represents and val.
 890          */
 891         private int getMatchCount(int val) {
 892             // This comes from BigInteger.bitCnt
 893             val &= state;
 894             val -= (0xaaaaaaaa & val) >>> 1;
 895             val = (val & 0x33333333) + ((val >>> 2) & 0x33333333);
 896             val = val + (val >>> 4) & 0x0f0f0f0f;
 897             val += val >>> 8;
 898             val += val >>> 16;
 899             return val & 0xff;
 900         }
 901 
 902         /**
 903          * Creates and returns a copy of this StateInfo.
 904          *
 905          * @return Copy of this StateInfo.
 906          */
 907         public Object clone() {
 908             return new StateInfo(this);
 909         }
 910 
 911         public String toString() {
 912             StringBuffer buf = new StringBuffer();
 913 
 914             buf.append(super.toString()).append(',');
 915 
 916             buf.append("state=").append(Integer.toString(state)).append(',');
 917 
 918             buf.append("font=").append(font).append(',');
 919 
 920             if (colors != null) {
 921                 buf.append("colors=").append(Arrays.asList(colors)).
 922                     append(',');
 923             }
 924             return buf.toString();
 925         }
 926     }
 927 }