View Javadoc
1   /*
2    * Copyright (c) 2002, 2013, 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 identifying 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 identifying 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 identifying 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 identifying 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 identifying 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 identifying 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 }