278N/A/*
278N/A * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
278N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
278N/A *
278N/A * This code is free software; you can redistribute it and/or modify it
278N/A * under the terms of the GNU General Public License version 2 only, as
278N/A * published by the Free Software Foundation. Oracle designates this
278N/A * particular file as subject to the "Classpath" exception as provided
278N/A * by Oracle in the LICENSE file that accompanied this code.
278N/A *
278N/A * This code is distributed in the hope that it will be useful, but WITHOUT
278N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
278N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
278N/A * version 2 for more details (a copy is included in the LICENSE file that
278N/A * accompanied this code).
278N/A *
278N/A * You should have received a copy of the GNU General Public License version
278N/A * 2 along with this work; if not, write to the Free Software Foundation,
278N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
278N/A *
278N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
278N/A * or visit www.oracle.com if you need additional information or have any
278N/A * questions.
278N/A */
278N/A
278N/Apackage sun.swing;
278N/A
278N/Aimport java.beans.*;
278N/Aimport java.lang.reflect.Method;
278N/A
278N/Apublic class BeanInfoUtils
278N/A{
278N/A /* The values of these createPropertyDescriptor() and
278N/A * createBeanDescriptor() keywords are the names of the
278N/A * properties they're used to set.
278N/A */
278N/A public static final String BOUND = "bound";
278N/A public static final String CONSTRAINED = "constrained";
278N/A public static final String PROPERTYEDITORCLASS = "propertyEditorClass";
278N/A public static final String READMETHOD = "readMethod";
278N/A public static final String WRITEMETHOD = "writeMethod";
278N/A public static final String DISPLAYNAME = "displayName";
278N/A public static final String EXPERT = "expert";
278N/A public static final String HIDDEN = "hidden";
278N/A public static final String PREFERRED = "preferred";
278N/A public static final String SHORTDESCRIPTION = "shortDescription";
278N/A public static final String CUSTOMIZERCLASS = "customizerClass";
278N/A
278N/A static private void initFeatureDescriptor(FeatureDescriptor fd, String key, Object value)
278N/A {
278N/A if (DISPLAYNAME.equals(key)) {
278N/A fd.setDisplayName((String)value);
278N/A }
278N/A
278N/A if (EXPERT.equals(key)) {
278N/A fd.setExpert(((Boolean)value).booleanValue());
278N/A }
278N/A
278N/A if (HIDDEN.equals(key)) {
278N/A fd.setHidden(((Boolean)value).booleanValue());
278N/A }
278N/A
278N/A if (PREFERRED.equals(key)) {
278N/A fd.setPreferred(((Boolean)value).booleanValue());
278N/A }
278N/A
278N/A else if (SHORTDESCRIPTION.equals(key)) {
278N/A fd.setShortDescription((String)value);
278N/A }
278N/A
278N/A /* Otherwise assume that we have an arbitrary FeatureDescriptor
278N/A * "attribute".
278N/A */
278N/A else {
278N/A fd.setValue(key, value);
278N/A }
278N/A }
278N/A
278N/A /**
278N/A * Create a beans PropertyDescriptor given an of keyword/value
278N/A * arguments. The following sample call shows all of the supported
278N/A * keywords:
278N/A *<pre>
278N/A * createPropertyDescriptor("contentPane", new Object[] {
278N/A * BOUND, Boolean.TRUE,
278N/A * CONSTRAINED, Boolean.TRUE,
278N/A * PROPERTYEDITORCLASS, package.MyEditor.class,
278N/A * READMETHOD, "getContentPane",
278N/A * WRITEMETHOD, "setContentPane",
278N/A * DISPLAYNAME, "contentPane",
278N/A * EXPERT, Boolean.FALSE,
278N/A * HIDDEN, Boolean.FALSE,
278N/A * PREFERRED, Boolean.TRUE,
278N/A * SHORTDESCRIPTION, "A top level window with a window manager border",
278N/A * "random attribute","random object value"
278N/A * }
278N/A * );
278N/A * </pre>
278N/A * The keywords correspond to <code>java.beans.PropertyDescriptor</code> and
278N/A * <code>java.beans.FeatureDescriptor</code> properties, e.g. providing a value
278N/A * for displayName is comparable to <code>FeatureDescriptor.setDisplayName()</code>.
278N/A * Using createPropertyDescriptor instead of the PropertyDescriptor
278N/A * constructor and set methods is preferrable in that it regularizes
278N/A * the code in a <code>java.beans.BeanInfo.getPropertyDescriptors()</code>
278N/A * method implementation. One can use <code>createPropertyDescriptor</code>
278N/A * to set <code>FeatureDescriptor</code> attributes, as in "random attribute"
278N/A * "random object value".
278N/A * <p>
278N/A * All properties should provide a reasonable value for the
278N/A * <code>SHORTDESCRIPTION</code> keyword and should set <code>BOUND</code>
278N/A * to <code>Boolean.TRUE</code> if neccessary. The remaining keywords
278N/A * are optional. There's no need to provide values for keywords like
* READMETHOD if the correct value can be computed, i.e. if the properties
* get/is method follows the standard beans pattern.
* <p>
* The PREFERRED keyword is not supported by the JDK1.1 java.beans package.
* It's still worth setting it to true for properties that are most
* likely to be interested to the average developer, e.g. AbstractButton.title
* is a preferred property, AbstractButton.focusPainted is not.
*
* @see java.beans#BeanInfo
* @see java.beans#PropertyDescriptor
* @see java.beans#FeatureDescriptor
*/
public static PropertyDescriptor createPropertyDescriptor(Class cls, String name, Object[] args)
{
PropertyDescriptor pd = null;
try {
pd = new PropertyDescriptor(name, cls);
} catch (IntrospectionException e) {
// Try creating a read-only property, in case setter isn't defined.
try {
pd = createReadOnlyPropertyDescriptor(name, cls);
} catch (IntrospectionException ie) {
throwError(ie, "Can't create PropertyDescriptor for " + name + " ");
}
}
for(int i = 0; i < args.length; i += 2) {
String key = (String)args[i];
Object value = args[i + 1];
if (BOUND.equals(key)) {
pd.setBound(((Boolean)value).booleanValue());
}
else if (CONSTRAINED.equals(key)) {
pd.setConstrained(((Boolean)value).booleanValue());
}
else if (PROPERTYEDITORCLASS.equals(key)) {
pd.setPropertyEditorClass((Class)value);
}
else if (READMETHOD.equals(key)) {
String methodName = (String)value;
Method method;
try {
method = cls.getMethod(methodName, new Class[0]);
pd.setReadMethod(method);
}
catch(Exception e) {
throwError(e, cls + " no such method as \"" + methodName + "\"");
}
}
else if (WRITEMETHOD.equals(key)) {
String methodName = (String)value;
Method method;
try {
Class type = pd.getPropertyType();
method = cls.getMethod(methodName, new Class[]{type});
pd.setWriteMethod(method);
}
catch(Exception e) {
throwError(e, cls + " no such method as \"" + methodName + "\"");
}
}
else {
initFeatureDescriptor(pd, key, value);
}
}
return pd;
}
/**
* Create a BeanDescriptor object given an of keyword/value
* arguments. The following sample call shows all of the supported
* keywords:
*<pre>
* createBeanDescriptor(JWindow..class, new Object[] {
* CUSTOMIZERCLASS, package.MyCustomizer.class,
* DISPLAYNAME, "JFrame",
* EXPERT, Boolean.FALSE,
* HIDDEN, Boolean.FALSE,
* PREFERRED, Boolean.TRUE,
* SHORTDESCRIPTION, "A top level window with a window manager border",
* "random attribute","random object value"
* }
* );
* </pre>
* The keywords correspond to <code>java.beans.BeanDescriptor</code> and
* <code>java.beans.FeatureDescriptor</code> properties, e.g. providing a value
* for displayName is comparable to <code>FeatureDescriptor.setDisplayName()</code>.
* Using createBeanDescriptor instead of the BeanDescriptor
* constructor and set methods is preferrable in that it regularizes
* the code in a <code>java.beans.BeanInfo.getBeanDescriptor()</code>
* method implementation. One can use <code>createBeanDescriptor</code>
* to set <code>FeatureDescriptor</code> attributes, as in "random attribute"
* "random object value".
*
* @see java.beans#BeanInfo
* @see java.beans#PropertyDescriptor
*/
public static BeanDescriptor createBeanDescriptor(Class cls, Object[] args)
{
Class customizerClass = null;
/* For reasons I don't understand, customizerClass is a
* readOnly property. So we have to find it and pass it
* to the constructor here.
*/
for(int i = 0; i < args.length; i += 2) {
if (CUSTOMIZERCLASS.equals((String)args[i])) {
customizerClass = (Class)args[i + 1];
break;
}
}
BeanDescriptor bd = new BeanDescriptor(cls, customizerClass);
for(int i = 0; i < args.length; i += 2) {
String key = (String)args[i];
Object value = args[i + 1];
initFeatureDescriptor(bd, key, value);
}
return bd;
}
static private PropertyDescriptor createReadOnlyPropertyDescriptor(
String name, Class cls) throws IntrospectionException {
Method readMethod = null;
String base = capitalize(name);
Class[] parameters = new Class[0];
// Is it a boolean?
try {
readMethod = cls.getMethod("is" + base, parameters);
} catch (Exception ex) {}
if (readMethod == null) {
try {
// Try normal accessor pattern.
readMethod = cls.getMethod("get" + base, parameters);
} catch (Exception ex2) {}
}
if (readMethod != null) {
return new PropertyDescriptor(name, readMethod, null);
}
try {
// Try indexed accessor pattern.
parameters = new Class[1];
parameters[0] = int.class;
readMethod = cls.getMethod("get" + base, parameters);
} catch (NoSuchMethodException nsme) {
throw new IntrospectionException(
"cannot find accessor method for " + name + " property.");
}
return new IndexedPropertyDescriptor(name, null, null, readMethod, null);
}
// Modified methods from java.beans.Introspector
private static String capitalize(String s) {
if (s.length() == 0) {
return s;
}
char chars[] = s.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
return new String(chars);
}
/**
* Fatal errors are handled by calling this method.
*/
public static void throwError(Exception e, String s) {
throw new Error(e.toString() + " " + s);
}
}