1 /* 2 * Copyright (c) 1998, 2014, 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 26 package sun.swing; 27 28 import java.beans.*; 29 import java.lang.reflect.Method; 30 31 public class BeanInfoUtils 32 { 33 /* The values of these createPropertyDescriptor() and 34 * createBeanDescriptor() keywords are the names of the 35 * properties they're used to set. 36 */ 37 public static final String BOUND = "bound"; 38 public static final String CONSTRAINED = "constrained"; 39 public static final String PROPERTYEDITORCLASS = "propertyEditorClass"; 40 public static final String READMETHOD = "readMethod"; 41 public static final String WRITEMETHOD = "writeMethod"; 42 public static final String DISPLAYNAME = "displayName"; 43 public static final String EXPERT = "expert"; 44 public static final String HIDDEN = "hidden"; 45 public static final String PREFERRED = "preferred"; 46 public static final String SHORTDESCRIPTION = "shortDescription"; 47 public static final String CUSTOMIZERCLASS = "customizerClass"; 48 49 static private void initFeatureDescriptor(FeatureDescriptor fd, String key, Object value) 50 { 51 if (DISPLAYNAME.equals(key)) { 52 fd.setDisplayName((String)value); 53 } 54 55 if (EXPERT.equals(key)) { 56 fd.setExpert(((Boolean)value).booleanValue()); 57 } 58 59 if (HIDDEN.equals(key)) { 60 fd.setHidden(((Boolean)value).booleanValue()); 61 } 62 63 if (PREFERRED.equals(key)) { 64 fd.setPreferred(((Boolean)value).booleanValue()); 65 } 66 67 else if (SHORTDESCRIPTION.equals(key)) { 68 fd.setShortDescription((String)value); 69 } 70 71 /* Otherwise assume that we have an arbitrary FeatureDescriptor 72 * "attribute". 73 */ 74 else { 75 fd.setValue(key, value); 76 } 77 } 78 79 /** 80 * Create a beans PropertyDescriptor given an of keyword/value 81 * arguments. The following sample call shows all of the supported 82 * keywords: 83 *<pre> 84 * createPropertyDescriptor("contentPane", new Object[] { 85 * BOUND, Boolean.TRUE, 86 * CONSTRAINED, Boolean.TRUE, 87 * PROPERTYEDITORCLASS, package.MyEditor.class, 88 * READMETHOD, "getContentPane", 89 * WRITEMETHOD, "setContentPane", 90 * DISPLAYNAME, "contentPane", 91 * EXPERT, Boolean.FALSE, 92 * HIDDEN, Boolean.FALSE, 93 * PREFERRED, Boolean.TRUE, 94 * SHORTDESCRIPTION, "A top level window with a window manager border", 95 * "random attribute","random object value" 96 * } 97 * ); 98 * </pre> 99 * The keywords correspond to <code>java.beans.PropertyDescriptor</code> and 100 * <code>java.beans.FeatureDescriptor</code> properties, e.g. providing a value 101 * for displayName is comparable to <code>FeatureDescriptor.setDisplayName()</code>. 102 * Using createPropertyDescriptor instead of the PropertyDescriptor 103 * constructor and set methods is preferrable in that it regularizes 104 * the code in a <code>java.beans.BeanInfo.getPropertyDescriptors()</code> 105 * method implementation. One can use <code>createPropertyDescriptor</code> 106 * to set <code>FeatureDescriptor</code> attributes, as in "random attribute" 107 * "random object value". 108 * <p> 109 * All properties should provide a reasonable value for the 110 * <code>SHORTDESCRIPTION</code> keyword and should set <code>BOUND</code> 111 * to <code>Boolean.TRUE</code> if neccessary. The remaining keywords 112 * are optional. There's no need to provide values for keywords like 113 * READMETHOD if the correct value can be computed, i.e. if the properties 114 * get/is method follows the standard beans pattern. 115 * <p> 116 * The PREFERRED keyword is not supported by the JDK1.1 java.beans package. 117 * It's still worth setting it to true for properties that are most 118 * likely to be interested to the average developer, e.g. AbstractButton.title 119 * is a preferred property, AbstractButton.focusPainted is not. 120 * 121 * @see java.beans#BeanInfo 122 * @see java.beans#PropertyDescriptor 123 * @see java.beans#FeatureDescriptor 124 */ 125 public static PropertyDescriptor createPropertyDescriptor(Class<?> cls, String name, Object[] args) 126 { 127 PropertyDescriptor pd = null; 128 try { 129 pd = new PropertyDescriptor(name, cls); 130 } catch (IntrospectionException e) { 131 // Try creating a read-only property, in case setter isn't defined. 132 try { 133 pd = createReadOnlyPropertyDescriptor(name, cls); 134 } catch (IntrospectionException ie) { 135 throwError(ie, "Can't create PropertyDescriptor for " + name + " "); 136 } 137 } 138 139 for(int i = 0; i < args.length; i += 2) { 140 String key = (String)args[i]; 141 Object value = args[i + 1]; 142 143 if (BOUND.equals(key)) { 144 pd.setBound(((Boolean)value).booleanValue()); 145 } 146 147 else if (CONSTRAINED.equals(key)) { 148 pd.setConstrained(((Boolean)value).booleanValue()); 149 } 150 151 else if (PROPERTYEDITORCLASS.equals(key)) { 152 pd.setPropertyEditorClass((Class)value); 153 } 154 155 else if (READMETHOD.equals(key)) { 156 String methodName = (String)value; 157 Method method; 158 try { 159 method = cls.getMethod(methodName, new Class<?>[0]); 160 pd.setReadMethod(method); 161 } 162 catch(Exception e) { 163 throwError(e, cls + " no such method as \"" + methodName + "\""); 164 } 165 } 166 167 else if (WRITEMETHOD.equals(key)) { 168 String methodName = (String)value; 169 Method method; 170 try { 171 Class<?> type = pd.getPropertyType(); 172 method = cls.getMethod(methodName, new Class<?>[]{type}); 173 pd.setWriteMethod(method); 174 } 175 catch(Exception e) { 176 throwError(e, cls + " no such method as \"" + methodName + "\""); 177 } 178 } 179 180 else { 181 initFeatureDescriptor(pd, key, value); 182 } 183 } 184 185 return pd; 186 } 187 188 189 /** 190 * Create a BeanDescriptor object given an of keyword/value 191 * arguments. The following sample call shows all of the supported 192 * keywords: 193 *<pre> 194 * createBeanDescriptor(JWindow..class, new Object[] { 195 * CUSTOMIZERCLASS, package.MyCustomizer.class, 196 * DISPLAYNAME, "JFrame", 197 * EXPERT, Boolean.FALSE, 198 * HIDDEN, Boolean.FALSE, 199 * PREFERRED, Boolean.TRUE, 200 * SHORTDESCRIPTION, "A top level window with a window manager border", 201 * "random attribute","random object value" 202 * } 203 * ); 204 * </pre> 205 * The keywords correspond to <code>java.beans.BeanDescriptor</code> and 206 * <code>java.beans.FeatureDescriptor</code> properties, e.g. providing a value 207 * for displayName is comparable to <code>FeatureDescriptor.setDisplayName()</code>. 208 * Using createBeanDescriptor instead of the BeanDescriptor 209 * constructor and set methods is preferrable in that it regularizes 210 * the code in a <code>java.beans.BeanInfo.getBeanDescriptor()</code> 211 * method implementation. One can use <code>createBeanDescriptor</code> 212 * to set <code>FeatureDescriptor</code> attributes, as in "random attribute" 213 * "random object value". 214 * 215 * @see java.beans#BeanInfo 216 * @see java.beans#PropertyDescriptor 217 */ 218 public static BeanDescriptor createBeanDescriptor(Class<?> cls, Object[] args) 219 { 220 Class<?> customizerClass = null; 221 222 /* For reasons I don't understand, customizerClass is a 223 * readOnly property. So we have to find it and pass it 224 * to the constructor here. 225 */ 226 for(int i = 0; i < args.length; i += 2) { 227 if (CUSTOMIZERCLASS.equals((String)args[i])) { 228 customizerClass = (Class)args[i + 1]; 229 break; 230 } 231 } 232 233 BeanDescriptor bd = new BeanDescriptor(cls, customizerClass); 234 235 for(int i = 0; i < args.length; i += 2) { 236 String key = (String)args[i]; 237 Object value = args[i + 1]; 238 initFeatureDescriptor(bd, key, value); 239 } 240 241 return bd; 242 } 243 244 static private PropertyDescriptor createReadOnlyPropertyDescriptor( 245 String name, Class<?> cls) throws IntrospectionException { 246 247 Method readMethod = null; 248 String base = capitalize(name); 249 Class<?>[] parameters = new Class<?>[0]; 250 251 // Is it a boolean? 252 try { 253 readMethod = cls.getMethod("is" + base, parameters); 254 } catch (Exception ex) {} 255 if (readMethod == null) { 256 try { 257 // Try normal accessor pattern. 258 readMethod = cls.getMethod("get" + base, parameters); 259 } catch (Exception ex2) {} 260 } 261 if (readMethod != null) { 262 return new PropertyDescriptor(name, readMethod, null); 263 } 264 265 try { 266 // Try indexed accessor pattern. 267 parameters = new Class<?>[1]; 268 parameters[0] = int.class; 269 readMethod = cls.getMethod("get" + base, parameters); 270 } catch (NoSuchMethodException nsme) { 271 throw new IntrospectionException( 272 "cannot find accessor method for " + name + " property."); 273 } 274 return new IndexedPropertyDescriptor(name, null, null, readMethod, null); 275 } 276 277 // Modified methods from java.beans.Introspector 278 private static String capitalize(String s) { 279 if (s.length() == 0) { 280 return s; 281 } 282 char chars[] = s.toCharArray(); 283 chars[0] = Character.toUpperCase(chars[0]); 284 return new String(chars); 285 } 286 287 /** 288 * Fatal errors are handled by calling this method. 289 */ 290 public static void throwError(Exception e, String s) { 291 throw new Error(e.toString() + " " + s); 292 } 293 }