001 /*
002 * Created on Dec 22, 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.exception.ActionFailedException.actionFailure;
019 import static org.fest.util.Collections.list;
020 import static org.fest.util.Strings.concat;
021 import static org.fest.util.Strings.quote;
022
023 import java.awt.Component;
024 import java.beans.*;
025 import java.util.*;
026
027 import org.fest.util.Arrays;
028
029 /**
030 * Understands a formatter that uses
031 * <a href="http://java.sun.com/docs/books/tutorial/javabeans/introspection/" target="_blank">introspection</a>
032 * to display property values of a <code>{@link Component}</code>. This formatter does not support nested properties.
033 *
034 * @author Alex Ruiz
035 */
036 public final class IntrospectionComponentFormatter extends ComponentFormatterTemplate {
037
038 private final Class<? extends Component> targetType;
039 private final List<String> propertyNames;
040
041 private final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
042
043 /**
044 * Creates a new </code>{@link IntrospectionComponentFormatter}</code>.
045 * @param targetType the type of <code>Component</code> that this formatter supports.
046 * @param propertyNames the property names to show as the <code>String</code> representation of a given
047 * <code>Component</code>.
048 * @throws NullPointerException if <code>targetType</code> is <code>null</code>.
049 */
050 public IntrospectionComponentFormatter(Class<? extends Component> targetType, String...propertyNames) {
051 if (targetType == null) throw new NullPointerException("targetType should not be null");
052 this.targetType = targetType;
053 this.propertyNames = list(propertyNames);
054 populate();
055 }
056
057 private void populate() {
058 BeanInfo beanInfo = null;
059 try {
060 beanInfo = Introspector.getBeanInfo(targetType, Object.class);
061 } catch (Exception e) {
062 throw actionFailure(concat("Unable to get BeanInfo for type ", targetType.getName()), e);
063 }
064 for (PropertyDescriptor d : beanInfo.getPropertyDescriptors()) register(d);
065 }
066
067 private void register(PropertyDescriptor d) {
068 String name = d.getName();
069 if (!propertyNames.contains(name)) return;
070 descriptors.put(name, d);
071 }
072
073 /**
074 * Returns a <code>String</code> representation of the given <code>{@link Component}</code>, showing only the
075 * properties specified in this formatter's
076 * <code>{@link #IntrospectionComponentFormatter(Class, String...) constructor}</code>.
077 * @param c the given <code>Component</code>.
078 * @return a <code>String</code> representation of the given <code>Component</code>.
079 * @throws NullPointerException if the given <code>Component</code> is <code>null</code>.
080 * @throws IllegalArgumentException if the type of the given <code>Component</code> is not supported by this
081 * formatter.
082 * @see #targetType()
083 */
084 protected String doFormat(Component c) {
085 StringBuilder b = new StringBuilder();
086 b.append(c.getClass().getName()).append("[");
087 int max = propertyNames.size() - 1;
088 for (int i = 0; i <= max; i++) {
089 appendProperty(b, propertyNames.get(i), c);
090 if (i < max) b.append(", ");
091 }
092 b.append("]");
093 return b.toString();
094 }
095
096 private void appendProperty(StringBuilder b, String name, Component c) {
097 b.append(name).append("=");
098 try {
099 b.append(propertyValue(c, name));
100 } catch (Exception e) {
101 b.append(concat("<Unable to read property [", e.getClass().getName(), ": ", quote(e.getMessage()), "]>"));
102 }
103 }
104
105 private Object propertyValue(Component c, String property) throws Exception {
106 if ("showing".equals(property)) return c.isShowing();
107 PropertyDescriptor descriptor = descriptors.get(property);
108 Object value = descriptor.getReadMethod().invoke(c);
109 if (isOneDimensionalArray(value)) return Arrays.format(value);
110 return quote(value);
111 }
112
113 private boolean isOneDimensionalArray(Object o) {
114 return o != null && o.getClass().isArray() && !o.getClass().getComponentType().isArray();
115 }
116
117 /**
118 * Returns the type of <code>{@link Component}</code> this formatter supports.
119 * @return the type of <code>Component</code> this formatter supports.
120 */
121 public Class<? extends Component> targetType() { return targetType; }
122
123 @Override public String toString() {
124 return concat(
125 getClass().getName(), "[",
126 "propertyNames=", propertyNames,
127 "]"
128 );
129 }
130 }