blob: fbcc2995dd5367adaa29a38a7b4d8b1504dcee9c [file] [log] [blame]
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.swing;
import java.beans.*;
import java.lang.reflect.Method;
public class BeanInfoUtils
{
/* The values of these createPropertyDescriptor() and
* createBeanDescriptor() keywords are the names of the
* properties they're used to set.
*/
public static final String BOUND = "bound";
public static final String CONSTRAINED = "constrained";
public static final String PROPERTYEDITORCLASS = "propertyEditorClass";
public static final String READMETHOD = "readMethod";
public static final String WRITEMETHOD = "writeMethod";
public static final String DISPLAYNAME = "displayName";
public static final String EXPERT = "expert";
public static final String HIDDEN = "hidden";
public static final String PREFERRED = "preferred";
public static final String SHORTDESCRIPTION = "shortDescription";
public static final String CUSTOMIZERCLASS = "customizerClass";
static private void initFeatureDescriptor(FeatureDescriptor fd, String key, Object value)
{
if (DISPLAYNAME.equals(key)) {
fd.setDisplayName((String)value);
}
if (EXPERT.equals(key)) {
fd.setExpert(((Boolean)value).booleanValue());
}
if (HIDDEN.equals(key)) {
fd.setHidden(((Boolean)value).booleanValue());
}
if (PREFERRED.equals(key)) {
fd.setPreferred(((Boolean)value).booleanValue());
}
else if (SHORTDESCRIPTION.equals(key)) {
fd.setShortDescription((String)value);
}
/* Otherwise assume that we have an arbitrary FeatureDescriptor
* "attribute".
*/
else {
fd.setValue(key, value);
}
}
/**
* Create a beans PropertyDescriptor given an of keyword/value
* arguments. The following sample call shows all of the supported
* keywords:
*<pre>
* createPropertyDescriptor("contentPane", new Object[] {
* BOUND, Boolean.TRUE,
* CONSTRAINED, Boolean.TRUE,
* PROPERTYEDITORCLASS, package.MyEditor.class,
* READMETHOD, "getContentPane",
* WRITEMETHOD, "setContentPane",
* DISPLAYNAME, "contentPane",
* 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.PropertyDescriptor</code> and
* <code>java.beans.FeatureDescriptor</code> properties, e.g. providing a value
* for displayName is comparable to <code>FeatureDescriptor.setDisplayName()</code>.
* Using createPropertyDescriptor instead of the PropertyDescriptor
* constructor and set methods is preferrable in that it regularizes
* the code in a <code>java.beans.BeanInfo.getPropertyDescriptors()</code>
* method implementation. One can use <code>createPropertyDescriptor</code>
* to set <code>FeatureDescriptor</code> attributes, as in "random attribute"
* "random object value".
* <p>
* All properties should provide a reasonable value for the
* <code>SHORTDESCRIPTION</code> keyword and should set <code>BOUND</code>
* to <code>Boolean.TRUE</code> if neccessary. The remaining keywords
* 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);
}
}