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 }