1 /*
   2  * Copyright (c) 2003, 2008, 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 javax.swing.plaf.synth;
  26 
  27 import javax.swing.*;
  28 import javax.swing.plaf.FontUIResource;
  29 import java.awt.Font;
  30 import java.util.*;
  31 import java.util.regex.*;
  32 import sun.swing.plaf.synth.*;
  33 import sun.swing.BakedArrayList;
  34 
  35 /**
  36  * Factory used for obtaining styles. Supports associating a style based on
  37  * the name of the component as returned by <code>Component.getName()</code>,
  38  * and the <code>Region</code> associated with the <code>JComponent</code>.
  39  * Lookup is done using regular expressions.
  40  *
  41  * @author Scott Violet
  42  */
  43 class DefaultSynthStyleFactory extends SynthStyleFactory {
  44     /**
  45      * Used to indicate the lookup should be done based on Component name.
  46      */
  47     public static final int NAME = 0;
  48     /**
  49      * Used to indicate the lookup should be done based on region.
  50      */
  51     public static final int REGION = 1;
  52 
  53     /**
  54      * List containing set of StyleAssociations used in determining matching
  55      * styles.
  56      */
  57     private List<StyleAssociation> _styles;
  58     /**
  59      * Used during lookup.
  60      */
  61     private BakedArrayList _tmpList;
  62 
  63     /**
  64      * Maps from a List (BakedArrayList to be precise) to the merged style.
  65      */
  66     private Map<BakedArrayList, SynthStyle> _resolvedStyles;
  67 
  68     /**
  69      * Used if there are no styles matching a widget.
  70      */
  71     private SynthStyle _defaultStyle;
  72 
  73 
  74     DefaultSynthStyleFactory() {
  75         _tmpList = new BakedArrayList(5);
  76         _styles = new ArrayList<StyleAssociation>();
  77         _resolvedStyles = new HashMap<BakedArrayList, SynthStyle>();
  78     }
  79 
  80     public synchronized void addStyle(DefaultSynthStyle style,
  81                          String path, int type) throws PatternSyntaxException {
  82         if (path == null) {
  83             // Make an empty path match all.
  84             path = ".*";
  85         }
  86         if (type == NAME) {
  87             _styles.add(StyleAssociation.createStyleAssociation(
  88                             path, style, type));
  89         }
  90         else if (type == REGION) {
  91             _styles.add(StyleAssociation.createStyleAssociation(
  92                             path.toLowerCase(), style, type));
  93         }
  94     }
  95 
  96     /**
  97      * Returns the style for the specified Component.
  98      *
  99      * @param c Component asking for
 100      * @param id ID of the Component
 101      */
 102     public synchronized SynthStyle getStyle(JComponent c, Region id) {
 103         BakedArrayList matches = _tmpList;
 104 
 105         matches.clear();
 106         getMatchingStyles(matches, c, id);
 107 
 108         if (matches.size() == 0) {
 109             return getDefaultStyle();
 110         }
 111         // Use a cached Style if possible, otherwise create a new one.
 112         matches.cacheHashCode();
 113         SynthStyle style = getCachedStyle(matches);
 114 
 115         if (style == null) {
 116             style = mergeStyles(matches);
 117 
 118             if (style != null) {
 119                 cacheStyle(matches, style);
 120             }
 121         }
 122         return style;
 123     }
 124 
 125     /**
 126      * Returns the style to use if there are no matching styles.
 127      */
 128     private SynthStyle getDefaultStyle() {
 129         if (_defaultStyle == null) {
 130             _defaultStyle = new DefaultSynthStyle();
 131             ((DefaultSynthStyle)_defaultStyle).setFont(
 132                 new FontUIResource(Font.DIALOG, Font.PLAIN,12));
 133         }
 134         return _defaultStyle;
 135     }
 136 
 137     /**
 138      * Fetches any styles that match the passed into arguments into
 139      * <code>matches</code>.
 140      */
 141     private void getMatchingStyles(List matches, JComponent c,
 142                                    Region id) {
 143         String idName = id.getLowerCaseName();
 144         String cName = c.getName();
 145 
 146         if (cName == null) {
 147             cName = "";
 148         }
 149         for (int counter = _styles.size() - 1; counter >= 0; counter--){
 150             StyleAssociation sa = _styles.get(counter);
 151             String path;
 152 
 153             if (sa.getID() == NAME) {
 154                 path = cName;
 155             }
 156             else {
 157                 path = idName;
 158             }
 159 
 160             if (sa.matches(path) && matches.indexOf(sa.getStyle()) == -1) {
 161                 matches.add(sa.getStyle());
 162             }
 163         }
 164     }
 165 
 166     /**
 167      * Caches the specified style.
 168      */
 169     private void cacheStyle(List styles, SynthStyle style) {
 170         BakedArrayList cachedStyles = new BakedArrayList(styles);
 171 
 172         _resolvedStyles.put(cachedStyles, style);
 173     }
 174 
 175     /**
 176      * Returns the cached style from the passed in arguments.
 177      */
 178     private SynthStyle getCachedStyle(List styles) {
 179         if (styles.size() == 0) {
 180             return null;
 181         }
 182         return _resolvedStyles.get(styles);
 183     }
 184 
 185     /**
 186      * Creates a single Style from the passed in styles. The passed in List
 187      * is reverse sorted, that is the most recently added style found to
 188      * match will be first.
 189      */
 190     private SynthStyle mergeStyles(List styles) {
 191         int size = styles.size();
 192 
 193         if (size == 0) {
 194             return null;
 195         }
 196         else if (size == 1) {
 197             return (SynthStyle)((DefaultSynthStyle)styles.get(0)).clone();
 198         }
 199         // NOTE: merging is done backwards as DefaultSynthStyleFactory reverses
 200         // order, that is, the most specific style is first.
 201         DefaultSynthStyle style = (DefaultSynthStyle)styles.get(size - 1);
 202 
 203         style = (DefaultSynthStyle)style.clone();
 204         for (int counter = size - 2; counter >= 0; counter--) {
 205             style = ((DefaultSynthStyle)styles.get(counter)).addTo(style);
 206         }
 207         return style;
 208     }
 209 }