001    /*
002     * Created on Sep 16, 2007
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     *
014     * Copyright @2007-2010 the original author or authors.
015     */
016    package org.fest.swing.format;
017    
018    import static org.fest.swing.edt.GuiActionRunner.execute;
019    import static org.fest.util.Strings.*;
020    
021    import java.awt.*;
022    import java.util.concurrent.ConcurrentHashMap;
023    import java.util.concurrent.ConcurrentMap;
024    import java.util.logging.Logger;
025    
026    import javax.swing.*;
027    import javax.swing.text.JTextComponent;
028    
029    import org.fest.swing.annotation.RunsInCurrentThread;
030    import org.fest.swing.annotation.RunsInEDT;
031    import org.fest.swing.edt.GuiQuery;
032    import org.fest.util.VisibleForTesting;
033    
034    /**
035     * Understands utility methods related to formatting.
036     *
037     * @author Alex Ruiz
038     * @author Yvonne Wang
039     */
040    public class Formatting {
041    
042    
043      private static final String MAXIMUM = "maximum";
044    
045      private static final String MINIMUM = "minimum";
046    
047      private static final String NULL_COMPONENT_MESSAGE = "Null Component";
048    
049      private static final String ENABLED = "enabled";
050      private static final String NAME = "name";
051      private static final String SHOWING = "showing";
052      private static final String TEXT = "text";
053      private static final String TITLE = "title";
054      private static final String VALUE = "value";
055      private static final String VISIBLE = "visible";
056    
057      private static final ConcurrentMap<Class<?>, ComponentFormatter> FORMATTERS = new ConcurrentHashMap<Class<?>, ComponentFormatter>();
058    
059      private static Logger logger = Logger.getLogger(Formatting.class.getName());
060    
061      static {
062        register(instrospect(AbstractButton.class, NAME, TEXT, "selected", ENABLED, VISIBLE, SHOWING));
063        register(instrospect(Dialog.class, NAME, TITLE, ENABLED, "modal", VISIBLE, SHOWING));
064        register(instrospect(Frame.class, NAME, TITLE, ENABLED, VISIBLE, SHOWING));
065        register(new JComboBoxFormatter());
066        register(instrospect(JButton.class, NAME, TEXT, ENABLED, VISIBLE, SHOWING));
067        register(new JFileChooserFormatter());
068        register(instrospect(JLabel.class, NAME, TEXT, ENABLED, VISIBLE, SHOWING));
069        register(empty(JLayeredPane.class));
070        register(new JListFormatter());
071        register(empty(JMenuBar.class));
072        register(new JOptionPaneFormatter());
073        register(nameOnly(JPanel.class));
074        register(instrospect(JPopupMenu.class, NAME, "label", ENABLED, VISIBLE, SHOWING));
075        register(instrospect(JProgressBar.class, NAME, VALUE, MINIMUM, MAXIMUM, "string", "stringPainted", ENABLED, VISIBLE, SHOWING));
076        register(empty(JRootPane.class));
077        register(instrospect(JScrollBar.class, NAME, VALUE, "blockIncrement", MINIMUM, MAXIMUM, ENABLED, VISIBLE, SHOWING));
078        register(instrospect(JScrollPane.class, NAME, ENABLED, VISIBLE, SHOWING));
079        register(instrospect(JSlider.class, NAME, VALUE, MINIMUM, MAXIMUM, ENABLED, VISIBLE, SHOWING));
080        register(instrospect(JSpinner.class, NAME, VALUE, ENABLED, VISIBLE, SHOWING));
081        register(new JTabbedPaneFormatter());
082        register(new JTableFormatter());
083        register(nameOnly(JToolBar.class));
084        register(instrospect(JPasswordField.class, NAME, ENABLED, VISIBLE, SHOWING));
085        register(instrospect(JTextComponent.class, NAME, TEXT, ENABLED, VISIBLE, SHOWING));
086        register(new JTreeFormatter());
087      }
088    
089      private static ComponentFormatter instrospect(Class<? extends Component> targetType, String...propertyNames) {
090        return new IntrospectionComponentFormatter(targetType, propertyNames);
091      }
092    
093      private static ComponentFormatter empty(Class<? extends Component> targetType) {
094        return new IntrospectionComponentFormatter(targetType);
095      }
096    
097      private static ComponentFormatter nameOnly(Class<? extends Component> targetType) {
098        return new IntrospectionComponentFormatter(targetType, NAME);
099      }
100    
101      /**
102       * Registers the given formatter, replacing any other one previously registered for the same supported component type.
103       * @param formatter the formatter to register.
104       */
105      public static void register(ComponentFormatter formatter) {
106        Class<?> key = formatter.targetType();
107        ComponentFormatter previous = FORMATTERS.put(key, formatter);
108        if (previous != null)
109          logger.info(
110              concat("Replaced formatter ", previous, " with ", formatter, " for the type ", key.getName()));
111      }
112    
113      @VisibleForTesting
114      static ComponentFormatter formatter(Class<?> type) {
115        return FORMATTERS.get(type);
116      }
117    
118      /**
119       * Returns a <code>String</code> representation of the given <code>{@link Component}</code>. This method is invoked in
120       * the event dispatch thread.
121       * @param c the given <code>Component</code>.
122       * @return a <code>String</code> representation of the given <code>Component</code>.
123       */
124      @RunsInEDT
125      public static String inEdtFormat(final Component c) {
126        return execute(new GuiQuery<String>() {
127          protected String executeInEDT() {
128            return format(c);
129          }
130        });
131      }
132    
133      /**
134       * Returns a <code>String</code> representation of the given <code>{@link Component}</code>.
135       * <p>
136       * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are
137       * responsible for calling this method from the EDT.
138       * </p>
139       * @param c the given <code>Component</code>.
140       * @return a <code>String</code> representation of the given <code>Component</code>.
141       */
142      @RunsInCurrentThread
143      public static String format(Component c) {
144        if (c == null) return NULL_COMPONENT_MESSAGE;
145        ComponentFormatter formatter = formatterFor(c.getClass());
146        if (formatter != null) return formatter.format(c);
147        String name = c.getName();
148        if (isEmpty(name)) return c.toString();
149        return concat(c.getClass().getName(), "[name=", quote(name), "]");
150      }
151    
152      private static ComponentFormatter formatterFor(Class<?> type) {
153        ComponentFormatter formatter = FORMATTERS.get(type);
154        if (formatter != null) return formatter;
155        Class<?> superType = type.getSuperclass();
156        if (superType != null) return formatterFor(superType);
157        return null;
158      }
159    
160      private Formatting() {}
161    }