4058433: RFE: tool for creating BeanInfo template

Reviewed-by: alexsch, serb
This commit is contained in:
Sergey Malenkov 2014-07-03 16:55:55 +04:00
parent 52f8e9c096
commit d5b9d36ad9
17 changed files with 1761 additions and 332 deletions

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2014, 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 com.sun.beans.introspect;
import com.sun.beans.util.Cache;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import static sun.reflect.misc.ReflectUtil.checkPackageAccess;
public final class ClassInfo {
private static final ClassInfo DEFAULT = new ClassInfo(null);
private static final Cache<Class<?>,ClassInfo> CACHE
= new Cache<Class<?>,ClassInfo>(Cache.Kind.SOFT, Cache.Kind.SOFT) {
@Override
public ClassInfo create(Class<?> type) {
return new ClassInfo(type);
}
};
public static ClassInfo get(Class<?> type) {
if (type == null) {
return DEFAULT;
}
try {
checkPackageAccess(type);
return CACHE.get(type);
} catch (SecurityException exception) {
return DEFAULT;
}
}
private final Object mutex = new Object();
private final Class<?> type;
private List<Method> methods;
private Map<String,PropertyInfo> properties;
private Map<String,EventSetInfo> eventSets;
private ClassInfo(Class<?> type) {
this.type = type;
}
public List<Method> getMethods() {
if (this.methods == null) {
synchronized (this.mutex) {
if (this.methods == null) {
this.methods = MethodInfo.get(this.type);
}
}
}
return this.methods;
}
public Map<String,PropertyInfo> getProperties() {
if (this.properties == null) {
synchronized (this.mutex) {
if (this.properties == null) {
this.properties = PropertyInfo.get(this.type);
}
}
}
return this.properties;
}
public Map<String,EventSetInfo> getEventSets() {
if (this.eventSets == null) {
synchronized (this.mutex) {
if (this.eventSets == null) {
this.eventSets = EventSetInfo.get(this.type);
}
}
}
return this.eventSets;
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2014, 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 com.sun.beans.introspect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.EventListener;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.TreeMap;
public final class EventSetInfo {
private MethodInfo add;
private MethodInfo remove;
private MethodInfo get;
private EventSetInfo() {
}
private boolean initialize() {
if ((this.add == null) || (this.remove == null) || (this.remove.type != this.add.type)) {
return false;
}
if ((this.get != null) && (this.get.type != this.add.type)) {
this.get = null;
}
return true;
}
public Class<?> getListenerType() {
return this.add.type;
}
public Method getAddMethod() {
return this.add.method;
}
public Method getRemoveMethod() {
return this.remove.method;
}
public Method getGetMethod() {
return (this.get == null) ? null : this.get.method;
}
public boolean isUnicast() {
// if the adder method throws the TooManyListenersException
// then it is an Unicast event source
return this.add.isThrow(TooManyListenersException.class);
}
private static MethodInfo getInfo(MethodInfo info, Method method, int prefix, int postfix) {
Class<?> type = (postfix > 0)
? MethodInfo.resolve(method, method.getGenericReturnType()).getComponentType()
: MethodInfo.resolve(method, method.getGenericParameterTypes()[0]);
if ((type != null) && EventListener.class.isAssignableFrom(type)) {
String name = method.getName();
if (prefix + postfix < name.length()) {
if (type.getName().endsWith(name.substring(prefix, name.length() - postfix))) {
if ((info == null) || info.type.isAssignableFrom(type)) {
return new MethodInfo(method, type);
}
}
}
}
return info;
}
private static EventSetInfo getInfo(Map<String,EventSetInfo> map, String key) {
EventSetInfo info = map.get(key);
if (info == null) {
info = new EventSetInfo();
map.put(key, info);
}
return info;
}
public static Map<String,EventSetInfo> get(Class<?> type) {
List<Method> methods = ClassInfo.get(type).getMethods();
if (methods.isEmpty()) {
return Collections.emptyMap();
}
Map<String,EventSetInfo> map = new TreeMap<>();
for (Method method : ClassInfo.get(type).getMethods()) {
if (!Modifier.isStatic(method.getModifiers())) {
Class<?> returnType = method.getReturnType();
String name = method.getName();
switch (method.getParameterCount()) {
case 1:
if ((returnType == void.class) && name.endsWith("Listener")) {
if (name.startsWith("add")) {
EventSetInfo info = getInfo(map, name.substring(3, name.length() - 8));
info.add = getInfo(info.add, method, 3, 0);
} else if (name.startsWith("remove")) {
EventSetInfo info = getInfo(map, name.substring(6, name.length() - 8));
info.remove = getInfo(info.remove, method, 6, 0);
}
}
break;
case 0:
if (returnType.isArray() && name.startsWith("get") && name.endsWith("Listeners")) {
EventSetInfo info = getInfo(map, name.substring(3, name.length() - 9));
info.get = getInfo(info.get, method, 3, 1);
}
break;
}
}
}
Iterator<EventSetInfo> iterator = map.values().iterator();
while (iterator.hasNext()) {
if (!iterator.next().initialize()) {
iterator.remove();
}
}
return !map.isEmpty()
? Collections.unmodifiableMap(map)
: Collections.emptyMap();
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2014, 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 com.sun.beans.introspect;
import com.sun.beans.TypeResolver;
import com.sun.beans.finder.MethodFinder;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
final class MethodInfo {
final Method method;
final Class<?> type;
MethodInfo(Method method, Class<?> type) {
this.method = method;
this.type = type;
}
MethodInfo(Method method, Type type) {
this.method = method;
this.type = resolve(method, type);
}
boolean isThrow(Class<?> exception) {
for (Class<?> type : this.method.getExceptionTypes()) {
if (type == exception) {
return true;
}
}
return false;
}
static Class<?> resolve(Method method, Type type) {
return TypeResolver.erase(TypeResolver.resolveInClass(method.getDeclaringClass(), type));
}
static List<Method> get(Class<?> type) {
List<Method> list = null;
if (type != null) {
boolean inaccessible = !Modifier.isPublic(type.getModifiers());
for (Method method : type.getMethods()) {
if (method.getDeclaringClass().equals(type)) {
if (inaccessible) {
try {
method = MethodFinder.findAccessibleMethod(method);
if (!method.getDeclaringClass().isInterface()) {
method = null; // ignore methods from superclasses
}
} catch (NoSuchMethodException exception) {
// commented out because of 6976577
// method = null; // ignore inaccessible methods
}
}
if (method != null) {
if (list == null) {
list = new ArrayList<>();
}
list.add(method);
}
}
}
}
return (list != null)
? Collections.unmodifiableList(list)
: Collections.emptyList();
}
}

View File

@ -0,0 +1,301 @@
/*
* Copyright (c) 2014, 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 com.sun.beans.introspect;
import java.beans.BeanProperty;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static com.sun.beans.finder.ClassFinder.findClass;
public final class PropertyInfo {
public enum Name {bound, expert, hidden, preferred, visualUpdate, description, enumerationValues}
private static final String VETO_EXCEPTION_NAME = "java.beans.PropertyVetoException";
private static final Class<?> VETO_EXCEPTION;
static {
Class<?> type;
try {
type = Class.forName(VETO_EXCEPTION_NAME);
} catch (Exception exception) {
type = null;
}
VETO_EXCEPTION = type;
}
private Class<?> type;
private MethodInfo read;
private MethodInfo write;
private PropertyInfo indexed;
private List<MethodInfo> readList;
private List<MethodInfo> writeList;
private Map<Name,Object> map;
private PropertyInfo() {
}
private boolean initialize() {
if (this.read != null) {
this.type = this.read.type;
}
if (this.readList != null) {
for (MethodInfo info : this.readList) {
if ((this.read == null) || this.read.type.isAssignableFrom(info.type)) {
this.read = info;
this.type = info.type;
}
}
this.readList = null;
}
if (this.writeList != null) {
for (MethodInfo info : this.writeList) {
if (this.type == null) {
this.write = info;
this.type = info.type;
} else if (this.type.isAssignableFrom(info.type)) {
if ((this.write == null) || this.write.type.isAssignableFrom(info.type)) {
this.write = info;
}
}
}
this.writeList = null;
}
if (this.indexed != null) {
if ((this.type != null) && !this.type.isArray()) {
this.indexed = null; // property type is not an array
} else if (!this.indexed.initialize()) {
this.indexed = null; // cannot initialize indexed methods
} else if ((this.type != null) && (this.indexed.type != this.type.getComponentType())) {
this.indexed = null; // different property types
} else {
this.map = this.indexed.map;
this.indexed.map = null;
}
}
if ((this.type == null) && (this.indexed == null)) {
return false;
}
initialize(this.write);
initialize(this.read);
return true;
}
private void initialize(MethodInfo info) {
if (info != null) {
BeanProperty annotation = info.method.getAnnotation(BeanProperty.class);
if (annotation != null) {
if (!annotation.bound()) {
put(Name.bound, Boolean.FALSE);
}
put(Name.expert, annotation.expert());
put(Name.hidden, annotation.hidden());
put(Name.preferred, annotation.preferred());
put(Name.visualUpdate, annotation.visualUpdate());
put(Name.description, annotation.description());
String[] values = annotation.enumerationValues();
if (0 < values.length) {
try {
Object[] array = new Object[3 * values.length];
int index = 0;
for (String value : values) {
Class<?> type = info.method.getDeclaringClass();
String name = value;
int pos = value.lastIndexOf('.');
if (pos > 0) {
name = value.substring(0, pos);
if (name.indexOf('.') < 0) {
String pkg = type.getName();
name = pkg.substring(0, 1 + Math.max(
pkg.lastIndexOf('.'),
pkg.lastIndexOf('$'))) + name;
}
type = findClass(name);
name = value.substring(pos + 1);
}
Field field = type.getField(name);
if (Modifier.isStatic(field.getModifiers()) && info.type.isAssignableFrom(field.getType())) {
array[index++] = name;
array[index++] = field.get(null);
array[index++] = value;
}
}
if (index == array.length) {
put(Name.enumerationValues, array);
}
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
}
}
}
public Class<?> getPropertyType() {
return this.type;
}
public Method getReadMethod() {
return (this.read == null) ? null : this.read.method;
}
public Method getWriteMethod() {
return (this.write == null) ? null : this.write.method;
}
public PropertyInfo getIndexed() {
return this.indexed;
}
public boolean isConstrained() {
if (this.write != null) {
if (VETO_EXCEPTION == null) {
for (Class<?> type : this.write.method.getExceptionTypes()) {
if (type.getName().equals(VETO_EXCEPTION_NAME)) {
return true;
}
}
} else if (this.write.isThrow(VETO_EXCEPTION)) {
return true;
}
}
return (this.indexed != null) && this.indexed.isConstrained();
}
public boolean is(Name name) {
Object value = get(name);
return (value instanceof Boolean)
? (Boolean) value
: Name.bound.equals(name);
}
public Object get(Name name) {
return this.map == null ? null : this.map.get(name);
}
private void put(Name name, boolean value) {
if (value) {
put(name, Boolean.TRUE);
}
}
private void put(Name name, String value) {
if (0 < value.length()) {
put(name, (Object) value);
}
}
private void put(Name name, Object value) {
if (this.map == null) {
this.map = new EnumMap<>(Name.class);
}
this.map.put(name, value);
}
private static List<MethodInfo> add(List<MethodInfo> list, Method method, Type type) {
if (list == null) {
list = new ArrayList<>();
}
list.add(new MethodInfo(method, type));
return list;
}
private static boolean isPrefix(String name, String prefix) {
return name.length() > prefix.length() && name.startsWith(prefix);
}
private static PropertyInfo getInfo(Map<String,PropertyInfo> map, String key, boolean indexed) {
PropertyInfo info = map.get(key);
if (info == null) {
info = new PropertyInfo();
map.put(key, info);
}
if (!indexed) {
return info;
}
if (info.indexed == null) {
info.indexed = new PropertyInfo();
}
return info.indexed;
}
public static Map<String,PropertyInfo> get(Class<?> type) {
List<Method> methods = ClassInfo.get(type).getMethods();
if (methods.isEmpty()) {
return Collections.emptyMap();
}
Map<String,PropertyInfo> map = new TreeMap<>();
for (Method method : methods) {
if (!Modifier.isStatic(method.getModifiers())) {
Class<?> returnType = method.getReturnType();
String name = method.getName();
switch (method.getParameterCount()) {
case 0:
if (returnType.equals(boolean.class) && isPrefix(name, "is")) {
PropertyInfo info = getInfo(map, name.substring(2), false);
info.read = new MethodInfo(method, boolean.class);
} else if (!returnType.equals(void.class) && isPrefix(name, "get")) {
PropertyInfo info = getInfo(map, name.substring(3), false);
info.readList = add(info.readList, method, method.getGenericReturnType());
}
break;
case 1:
if (returnType.equals(void.class) && isPrefix(name, "set")) {
PropertyInfo info = getInfo(map, name.substring(3), false);
info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[0]);
} else if (!returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "get")) {
PropertyInfo info = getInfo(map, name.substring(3), true);
info.readList = add(info.readList, method, method.getGenericReturnType());
}
break;
case 2:
if (returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "set")) {
PropertyInfo info = getInfo(map, name.substring(3), true);
info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[1]);
}
break;
}
}
}
Iterator<PropertyInfo> iterator = map.values().iterator();
while (iterator.hasNext()) {
if (!iterator.next().initialize()) {
iterator.remove();
}
}
return !map.isEmpty()
? Collections.unmodifiableMap(map)
: Collections.emptyMap();
}
}

View File

@ -22,10 +22,10 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.beans;
import java.lang.ref.Reference;
import javax.swing.SwingContainer;
/**
* A BeanDescriptor provides global information about a "bean",
@ -69,6 +69,23 @@ public class BeanDescriptor extends FeatureDescriptor {
name = name.substring(name.indexOf('.')+1);
}
setName(name);
JavaBean annotation = beanClass.getAnnotation(JavaBean.class);
if (annotation != null) {
setPreferred(true);
String description = annotation.description();
if (!description.isEmpty()) {
setShortDescription(description);
}
}
SwingContainer container = beanClass.getAnnotation(SwingContainer.class);
if (container != null) {
setValue("isContainer", container.value());
String delegate = container.delegate();
if (!delegate.isEmpty()) {
setValue("containerDelegate", delegate);
}
}
}
/**

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2014, 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 java.beans;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* An annotation used to specify some property-related information
* for the automatically generated {@link BeanInfo} classes.
* This annotation is not used if the annotated class
* has a corresponding user-defined {@code BeanInfo} class,
* which does not imply the automatic analysis.
*
* @see BeanInfo#getPropertyDescriptors
* @since 1.9
*
* @author Sergey A. Malenkov
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface BeanProperty {
/**
* The value that indicates whether the annotated property can be
* a {@link PropertyDescriptor#isBound bound} property or not.
* This value applies only to the beans that have the
* {@link PropertyChangeListener propertyChange} event set.
*
* @return {@code true} if the annotated property can be a bound property;
* {@code false} otherwise.
*/
boolean bound() default true;
/**
* The value that indicates whether the annotated property is
* an {@link PropertyDescriptor#isExpert expert} property or not.
*
* @return {@code true} if the annotated property is an expert property;
* {@code false} otherwise.
*/
boolean expert() default false;
/**
* The value that indicates whether the annotated property is
* a {@link PropertyDescriptor#isHidden hidden} property or not.
*
* @return {@code true} if the annotated property is a hidden property;
* {@code false} otherwise.
*/
boolean hidden() default false;
/**
* The value that indicates whether the annotated property is
* a {@link PropertyDescriptor#isPreferred preferred} property or not.
*
* @return {@code true} if the annotated property is a preferred property;
* {@code false} otherwise.
*/
boolean preferred() default false;
/**
* The value that indicates whether the annotated property is
* a required property or not.
*
* @return {@code true} if the annotated property is a required property;
* {@code false} otherwise.
*/
boolean required() default false;
/**
* The value that indicates whether the corresponding component
* is repainted after the annotated property got changed or not.
*
* @return {@code true} if the corresponding component is repainted;
* {@code false} otherwise.
*/
boolean visualUpdate() default false;
/**
* The {@link PropertyDescriptor#getShortDescription short description}
* for the {@link BeanInfo#getPropertyDescriptors descriptor}
* of the annotated property.
*
* @return the property description,
* or an empty string if the description is not set.
*/
String description() default "";
/**
* The array of names for the public static fields
* that contains the valid values of the annotated property.
* These names are used to generate the {@code enumerationValues}
* {@link java.beans.BeanDescriptor#getValue feature attribute}
* that must contain the following items per each property value:
* a displayable name for the property value, the actual property value,
* and a Java code piece used for the code generator.
*
* @return the names of the valid values of the annotated property,
* or an empty array if the names are not provided.
*/
String[] enumerationValues() default {};
}

View File

@ -22,13 +22,14 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.beans;
import java.lang.ref.Reference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.sun.beans.introspect.EventSetInfo;
/**
* An EventSetDescriptor describes a group of events that a given Java
* bean fires.
@ -255,6 +256,16 @@ public class EventSetDescriptor extends FeatureDescriptor {
setListenerType(listenerType);
}
EventSetDescriptor(String base, EventSetInfo info, Method... methods) {
setName(Introspector.decapitalize(base));
setListenerMethods(methods);
setAddListenerMethod(info.getAddMethod());
setRemoveListenerMethod(info.getRemoveMethod());
setGetListenerMethod(info.getGetMethod());
setListenerType(info.getListenerType());
setUnicast(info.isUnicast());
}
/**
* Creates an <TT>EventSetDescriptor</TT> from scratch using
* <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT>

View File

@ -22,11 +22,13 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.beans;
import java.lang.ref.Reference;
import java.lang.reflect.Method;
import java.util.Map.Entry;
import com.sun.beans.introspect.PropertyInfo;
/**
* An IndexedPropertyDescriptor describes a property that acts like an
@ -143,27 +145,21 @@ public class IndexedPropertyDescriptor extends PropertyDescriptor {
}
/**
* Creates <code>PropertyDescriptor</code> for the specified bean
* with the specified name and methods to read/write the property value.
* Creates {@code IndexedPropertyDescriptor} from the specified property info.
*
* @param bean the type of the target bean
* @param base the base name of the property (the rest of the method name)
* @param read the method used for reading the property value
* @param write the method used for writing the property value
* @param readIndexed the method used for reading an indexed property value
* @param writeIndexed the method used for writing an indexed property value
* @exception IntrospectionException if an exception occurs during introspection
* @param entry the key-value pair,
* where the {@code key} is the base name of the property (the rest of the method name)
* and the {@code value} is the automatically generated property info
* @param bound the flag indicating whether it is possible to treat this property as a bound property
*
* @since 1.7
* @since 1.9
*/
IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException {
super(bean, base, read, write);
setIndexedReadMethod0(readIndexed);
setIndexedWriteMethod0(writeIndexed);
// Type checking
setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed));
IndexedPropertyDescriptor(Entry<String,PropertyInfo> entry, boolean bound) {
super(entry, bound);
PropertyInfo info = entry.getValue().getIndexed();
setIndexedReadMethod0(info.getReadMethod());
setIndexedWriteMethod0(info.getWriteMethod());
setIndexedPropertyType(info.getPropertyType());
}
/**

View File

@ -22,32 +22,30 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.beans;
import com.sun.beans.TypeResolver;
import com.sun.beans.WeakCache;
import com.sun.beans.finder.ClassFinder;
import com.sun.beans.finder.MethodFinder;
import com.sun.beans.introspect.ClassInfo;
import com.sun.beans.introspect.EventSetInfo;
import com.sun.beans.introspect.PropertyInfo;
import java.awt.Component;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;
import java.util.TreeMap;
import sun.misc.JavaBeansIntrospectorAccess;
import sun.misc.SharedSecrets;
import sun.reflect.misc.ReflectUtil;
@ -122,7 +120,6 @@ public class Introspector {
private BeanInfo additionalBeanInfo[];
private boolean propertyChangeSource = false;
private static Class<EventListener> eventListenerType = EventListener.class;
// These should be removed.
private String defaultEventName;
@ -501,78 +498,15 @@ public class Introspector {
addPropertyDescriptors(explicitProperties);
} else {
// Apply some reflection to the current class.
// First get an array of all the public methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);
// Now analyze each method.
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
// skip static methods.
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
continue;
}
String name = method.getName();
Class<?>[] argTypes = method.getParameterTypes();
Class<?> resultType = method.getReturnType();
int argCount = argTypes.length;
PropertyDescriptor pd = null;
if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
// Optimization. Don't bother with invalid propertyNames.
continue;
}
try {
if (argCount == 0) {
if (name.startsWith(GET_PREFIX)) {
// Simple getter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
// Boolean getter
pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
}
} else if (argCount == 1) {
if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
} else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
// Simple setter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
if (throwsException(method, PropertyVetoException.class)) {
pd.setConstrained(true);
}
}
} else if (argCount == 2) {
if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {
pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);
if (throwsException(method, PropertyVetoException.class)) {
pd.setConstrained(true);
}
}
}
} catch (IntrospectionException ex) {
// This happens if a PropertyDescriptor or IndexedPropertyDescriptor
// constructor fins that the method violates details of the deisgn
// pattern, e.g. by having an empty name, or a getter returning
// void , or whatever.
pd = null;
}
if (pd != null) {
// If this class or one of its base classes is a PropertyChange
// source, then we assume that any properties we discover are "bound".
if (propertyChangeSource) {
pd.setBound(true);
}
addPropertyDescriptor(pd);
}
for (Map.Entry<String,PropertyInfo> entry : ClassInfo.get(this.beanClass).getProperties().entrySet()) {
addPropertyDescriptor(null != entry.getValue().getIndexed()
? new IndexedPropertyDescriptor(entry, this.propertyChangeSource)
: new PropertyDescriptor(entry, this.propertyChangeSource));
}
JavaBean annotation = this.beanClass.getAnnotation(JavaBean.class);
if ((annotation != null) && !annotation.defaultProperty().isEmpty()) {
this.defaultPropertyName = annotation.defaultProperty();
}
}
processPropertyDescriptors();
@ -589,7 +523,6 @@ public class Introspector {
}
}
}
return result;
}
@ -1003,143 +936,24 @@ public class Introspector {
}
} else {
// Apply some reflection to the current class.
// Get an array of all the public beans methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);
// Find all suitable "add", "remove" and "get" Listener methods
// The name of the listener type is the key for these hashtables
// i.e, ActionListener
Map<String, Method> adds = null;
Map<String, Method> removes = null;
Map<String, Method> gets = null;
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
// skip static methods.
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
continue;
}
String name = method.getName();
// Optimization avoid getParameterTypes
if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)
&& !name.startsWith(GET_PREFIX)) {
continue;
}
if (name.startsWith(ADD_PREFIX)) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
Type[] parameterTypes = method.getGenericParameterTypes();
if (parameterTypes.length == 1) {
Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(3);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (adds == null) {
adds = new HashMap<>();
}
adds.put(listenerName, method);
}
}
}
}
}
else if (name.startsWith(REMOVE_PREFIX)) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
Type[] parameterTypes = method.getGenericParameterTypes();
if (parameterTypes.length == 1) {
Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(6);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (removes == null) {
removes = new HashMap<>();
}
removes.put(listenerName, method);
}
}
}
}
}
else if (name.startsWith(GET_PREFIX)) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
Class<?> returnType = FeatureDescriptor.getReturnType(beanClass, method);
if (returnType.isArray()) {
Class<?> type = returnType.getComponentType();
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(3, name.length() - 1);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (gets == null) {
gets = new HashMap<>();
}
gets.put(listenerName, method);
}
}
}
}
}
}
if (adds != null && removes != null) {
// Now look for matching addFooListener+removeFooListener pairs.
// Bonus if there is a matching getFooListeners method as well.
Iterator<String> keys = adds.keySet().iterator();
while (keys.hasNext()) {
String listenerName = keys.next();
// Skip any "add" which doesn't have a matching "remove" or
// a listener name that doesn't end with Listener
if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
continue;
}
String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
Method addMethod = adds.get(listenerName);
Method removeMethod = removes.get(listenerName);
Method getMethod = null;
if (gets != null) {
getMethod = gets.get(listenerName);
}
Class<?> argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0];
for (Map.Entry<String,EventSetInfo> entry : ClassInfo.get(this.beanClass).getEventSets().entrySet()) {
// generate a list of Method objects for each of the target methods:
Method allMethods[] = getPublicDeclaredMethods(argType);
List<Method> validMethods = new ArrayList<>(allMethods.length);
for (int i = 0; i < allMethods.length; i++) {
if (allMethods[i] == null) {
continue;
}
if (isEventHandler(allMethods[i])) {
validMethods.add(allMethods[i]);
}
List<Method> methods = new ArrayList<>();
for (Method method : ClassInfo.get(entry.getValue().getListenerType()).getMethods()) {
if (isEventHandler(method)) {
methods.add(method);
}
Method[] methods = validMethods.toArray(new Method[validMethods.size()]);
EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,
methods, addMethod,
removeMethod,
getMethod);
// If the adder method throws the TooManyListenersException then it
// is a Unicast event source.
if (throwsException(addMethod,
java.util.TooManyListenersException.class)) {
esd.setUnicast(true);
}
addEvent(esd);
}
} // if (adds != null ...
addEvent(new EventSetDescriptor(
entry.getKey(),
entry.getValue(),
methods.toArray(new Method[methods.size()])));
}
JavaBean annotation = this.beanClass.getAnnotation(JavaBean.class);
if ((annotation != null) && !annotation.defaultEventSet().isEmpty()) {
this.defaultEventName = annotation.defaultEventSet();
}
}
EventSetDescriptor[] result;
if (events.size() == 0) {
@ -1148,7 +962,6 @@ public class Introspector {
// Allocate and populate the result array.
result = new EventSetDescriptor[events.size()];
result = events.values().toArray(result);
// Set the default index.
if (defaultEventName != null) {
for (int i = 0; i < result.length; i++) {
@ -1215,20 +1028,9 @@ public class Introspector {
}
} else {
// Apply some reflection to the current class.
// First get an array of all the beans methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);
// Now analyze each method.
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
MethodDescriptor md = new MethodDescriptor(method);
addMethod(md);
for (Method method : ClassInfo.get(this.beanClass).getMethods()) {
addMethod(new MethodDescriptor(method));
}
}
@ -1346,44 +1148,6 @@ public class Introspector {
return isSubclass(TypeResolver.erase(TypeResolver.resolveInClass(beanClass, argTypes[0])), EventObject.class);
}
/*
* Internal method to return *public* methods within a class.
*/
private static Method[] getPublicDeclaredMethods(Class<?> clz) {
// Looking up Class.getDeclaredMethods is relatively expensive,
// so we cache the results.
if (!ReflectUtil.isPackageAccessible(clz)) {
return new Method[0];
}
synchronized (declaredMethodCache) {
Method[] result = declaredMethodCache.get(clz);
if (result == null) {
result = clz.getMethods();
for (int i = 0; i < result.length; i++) {
Method method = result[i];
if (!method.getDeclaringClass().equals(clz)) {
result[i] = null; // ignore methods declared elsewhere
}
else {
try {
method = MethodFinder.findAccessibleMethod(method);
Class<?> type = method.getDeclaringClass();
result[i] = type.equals(clz) || type.isInterface()
? method
: null; // ignore methods from superclasses
}
catch (NoSuchMethodException exception) {
// commented out because of 6976577
// result[i] = null; // ignore inaccessible methods
}
}
}
declaredMethodCache.put(clz, result);
}
return result;
}
}
//======================================================================
// Package private support methods.
//======================================================================
@ -1396,17 +1160,8 @@ public class Introspector {
int argCount, Class<?> args[]) {
// For overriden methods we need to find the most derived version.
// So we start with the given class and walk up the superclass chain.
Method method = null;
for (Class<?> cl = start; cl != null; cl = cl.getSuperclass()) {
Method methods[] = getPublicDeclaredMethods(cl);
for (int i = 0; i < methods.length; i++) {
method = methods[i];
if (method == null) {
continue;
}
for (Method method : ClassInfo.get(cl).getMethods()) {
// make sure method signature matches.
if (method.getName().equals(methodName)) {
Type[] params = method.getGenericParameterTypes();
@ -1430,8 +1185,6 @@ public class Introspector {
}
}
}
method = null;
// Now check any inherited interfaces. This is necessary both when
// the argument class is itself an interface, and when the argument
// class is an abstract class.
@ -1440,12 +1193,12 @@ public class Introspector {
// Note: The original implementation had both methods calling
// the 3 arg method. This is preserved but perhaps it should
// pass the args array instead of null.
method = internalFindMethod(ifcs[i], methodName, argCount, null);
Method method = internalFindMethod(ifcs[i], methodName, argCount, null);
if (method != null) {
break;
return method;
}
}
return method;
return null;
}
/**
@ -1507,19 +1260,6 @@ public class Introspector {
return false;
}
/**
* Return true iff the given method throws the given exception.
*/
private boolean throwsException(Method method, Class<?> exception) {
Class<?>[] exs = method.getExceptionTypes();
for (int i = 0; i < exs.length; i++) {
if (exs[i] == exception) {
return true;
}
}
return false;
}
/**
* Try to create an instance of a named class.
* First try the classloader of "sibling", then try the system

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2014, 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 java.beans;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* An annotation used to specify some class-related information
* for the automatically generated {@link BeanInfo} classes.
* This annotation is not used if the annotated class
* has a corresponding user-defined {@code BeanInfo} class,
* which does not imply the automatic analysis.
*
* @see BeanInfo#getBeanDescriptor
* @since 1.9
*
* @author Sergey A. Malenkov
*/
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface JavaBean {
/**
* The {@link BeanDescriptor#getShortDescription short description}
* for the {@link BeanInfo#getBeanDescriptor bean descriptor}
* of the annotated class.
*
* @return the bean description,
* or an empty string if the description is not set.
*/
String description() default "";
/**
* The name of the default property is used to calculate its
* {@link BeanInfo#getDefaultPropertyIndex index} in the
* {@link BeanInfo#getPropertyDescriptors array} of properties
* defined in the annotated class. If the name is not set or
* the annotated class does not define a property
* with the specified name, the default property index
* will be calculated automatically by the
* {@link Introspector} depending on its state.
*
* @return the name of the default property,
* or an empty string if the name is not set.
*/
String defaultProperty() default "";
/**
* The name of the default event set is used to calculate its
* {@link BeanInfo#getDefaultEventIndex index} in the
* {@link BeanInfo#getEventSetDescriptors array} of event sets
* defined in the annotated class. If the name is not set or
* the annotated class does not define an event set
* with the specified name, the default event set index
* will be calculated automatically by the
* {@link Introspector} depending on its state.
*
* @return the name of the default event set,
* or an empty string if the name is not set.
*/
String defaultEventSet() default "";
}

View File

@ -22,12 +22,14 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.beans;
import java.lang.ref.Reference;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.util.Map.Entry;
import com.sun.beans.introspect.PropertyInfo;
/**
* A PropertyDescriptor describes one property that a Java Bean
@ -140,25 +142,47 @@ public class PropertyDescriptor extends FeatureDescriptor {
}
/**
* Creates <code>PropertyDescriptor</code> for the specified bean
* with the specified name and methods to read/write the property value.
* Creates {@code PropertyDescriptor} from the specified property info.
*
* @param bean the type of the target bean
* @param base the base name of the property (the rest of the method name)
* @param read the method used for reading the property value
* @param write the method used for writing the property value
* @exception IntrospectionException if an exception occurs during introspection
* @param entry the pair of values,
* where the {@code key} is the base name of the property (the rest of the method name)
* and the {@code value} is the automatically generated property info
* @param bound the flag indicating whether it is possible to treat this property as a bound property
*
* @since 1.7
* @since 1.9
*/
PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException {
if (bean == null) {
throw new IntrospectionException("Target Bean class is null");
}
setClass0(bean);
PropertyDescriptor(Entry<String,PropertyInfo> entry, boolean bound) {
String base = entry.getKey();
PropertyInfo info = entry.getValue();
setName(Introspector.decapitalize(base));
setReadMethod(read);
setWriteMethod(write);
setReadMethod0(info.getReadMethod());
setWriteMethod0(info.getWriteMethod());
setPropertyType(info.getPropertyType());
setConstrained(info.isConstrained());
setBound(bound && info.is(PropertyInfo.Name.bound));
if (info.is(PropertyInfo.Name.expert)) {
setValue(PropertyInfo.Name.expert.name(), Boolean.TRUE); // compatibility
setExpert(true);
}
if (info.is(PropertyInfo.Name.hidden)) {
setValue(PropertyInfo.Name.hidden.name(), Boolean.TRUE); // compatibility
setHidden(true);
}
if (info.is(PropertyInfo.Name.preferred)) {
setPreferred(true);
}
Object visual = info.get(PropertyInfo.Name.visualUpdate);
if (visual != null) {
setValue(PropertyInfo.Name.visualUpdate.name(), visual);
}
Object description = info.get(PropertyInfo.Name.description);
if (description != null) {
setShortDescription(description.toString());
}
Object values = info.get(PropertyInfo.Name.enumerationValues);
if (values != null) {
setValue(PropertyInfo.Name.enumerationValues.name(), values);
}
this.baseName = base;
}
@ -249,13 +273,17 @@ public class PropertyDescriptor extends FeatureDescriptor {
*/
public synchronized void setReadMethod(Method readMethod)
throws IntrospectionException {
// The property type is determined by the read method.
setPropertyType(findPropertyType(readMethod, this.writeMethodRef.get()));
setReadMethod0(readMethod);
}
private void setReadMethod0(Method readMethod) {
this.readMethodRef.set(readMethod);
if (readMethod == null) {
readMethodName = null;
return;
}
// The property type is determined by the read method.
setPropertyType(findPropertyType(readMethod, this.writeMethodRef.get()));
setClass0(readMethod.getDeclaringClass());
readMethodName = readMethod.getName();
@ -320,13 +348,17 @@ public class PropertyDescriptor extends FeatureDescriptor {
*/
public synchronized void setWriteMethod(Method writeMethod)
throws IntrospectionException {
// Set the property type - which validates the method
setPropertyType(findPropertyType(getReadMethod(), writeMethod));
setWriteMethod0(writeMethod);
}
private void setWriteMethod0(Method writeMethod) {
this.writeMethodRef.set(writeMethod);
if (writeMethod == null) {
writeMethodName = null;
return;
}
// Set the property type - which validates the method
setPropertyType(findPropertyType(getReadMethod(), writeMethod));
setClass0(writeMethod.getDeclaringClass());
writeMethodName = writeMethod.getName();

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2014, 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 javax.swing;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* An annotation used to specify some swing-related information
* for the automatically generated {@code BeanInfo} classes.
* This annotation is not used if the annotated class
* has a corresponding user-defined {@code BeanInfo} class,
* which does not imply the automatic analysis.
* <p>
* The {@code isContainer} {@link java.beans.BeanDescriptor#getValue
* feature attribute} was introduced primarily for the Swing library.
* All Swing components extend the {@link java.awt.Container Container}
* class by design, so the builder tool assumes that all Swing components
* are containers. The {@link java.beans.BeanInfo BeanInfo} classes
* with the {@code isContainer} attribute allow to directly specify
* whether a Swing component is a container or not.
*
* @since 1.9
*
* @author Sergey A. Malenkov
*/
@Target({TYPE})
@Retention(RUNTIME)
public @interface SwingContainer {
/**
* The value that indicates whether the annotated class can be used
* as a container for other Swing components or not.
*
* @return {@code true} if the annotated class is a Swing container;
* {@code false} otherwise.
*/
boolean value() default true;
/**
* The name of the getter method in the annotated class,
* which returns the corresponding Swing container,
* if it is not recommended to add subcomponents
* to the annotated class directly.
*
* @return the name of the getter method in the annotated class,
* which returns the corresponding Swing container,
* or an empty string if the method name is not set.
*/
String delegate() default "";
}

View File

@ -0,0 +1,272 @@
/*
* Copyright (c) 2014, 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.
*
* 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.
*/
import java.beans.BeanProperty;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
/*
* @test
* @bug 4058433
* @summary Tests the BeanProperty annotation
* @author Sergey Malenkov
* @library ..
*/
public class TestBeanProperty {
public static void main(String[] args) throws Exception {
Class<?>[] types = {B.class, BL.class, BLF.class, E.class, H.class, P.class, VU.class, D.class, EV.class, EVL.class, EVX.class};
for (Class<?> type : types) {
PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(type, "value");
if (((B.class == type) || (BLF.class == type)) && pd.isBound()) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("not bound");
}
if ((BL.class == type) == !pd.isBound()) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("bound");
}
if ((E.class == type) == !pd.isExpert()) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("expert");
}
if ((H.class == type) == !pd.isHidden()) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("hidden");
}
if ((P.class == type) == !pd.isPreferred()) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("preferred");
}
if ((R.class == type) == !Boolean.TRUE.equals(pd.getValue("required"))) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("required");
}
if ((VU.class == type) == !Boolean.TRUE.equals(pd.getValue("visualUpdate"))) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("visualUpdate");
}
if ((EV.class == type) == !isEV(pd, "LEFT", 2, "javax.swing.SwingConstants.LEFT", "RIGHT", 4, "javax.swing.SwingConstants.RIGHT")) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("enumerationValues from another package");
}
if ((EVL.class == type) == !isEV(pd, "ZERO", 0, "ZERO", "ONE", 1, "ONE")) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("enumerationValues from another package");
}
if ((EVX.class == type) == !isEV(pd, "ZERO", 0, "X.ZERO", "ONE", 1, "X.ONE")) {
BeanUtils.reportPropertyDescriptor(pd);
throw new Error("enumerationValues from another package");
}
}
}
private static boolean isEV(PropertyDescriptor pd, Object... expected) {
Object value = pd.getValue("enumerationValues");
return value instanceof Object[] && Arrays.equals((Object[]) value, expected);
}
public static class B {
private int value;
public int getValue() {
return this.value;
}
public void setValue(int value) {
this.value = value;
}
}
public static class BL {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private int value;
public int getValue() {
return this.value;
}
public void setValue(int value) {
this.value = value;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
}
public static class BLF {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(bound = false)
public void setValue(int value) {
this.value = value;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
}
public static class E {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(expert = true)
public void setValue(int value) {
this.value = value;
}
}
public static class H {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(hidden = true)
public void setValue(int value) {
this.value = value;
}
}
public static class P {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(preferred = true)
public void setValue(int value) {
this.value = value;
}
}
public static class R {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(required = true)
public void setValue(int value) {
this.value = value;
}
}
public static class VU {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(visualUpdate = true)
public void setValue(int value) {
this.value = value;
}
}
public static class D {
private int value;
@BeanProperty(description = "getter")
public int getValue() {
return this.value;
}
@BeanProperty(description = "setter")
public void setValue(int value) {
this.value = value;
}
}
public static class EV {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(enumerationValues = {
"javax.swing.SwingConstants.LEFT",
"javax.swing.SwingConstants.RIGHT"})
public void setValue(int value) {
this.value = value;
}
}
public static class EVL {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(enumerationValues = {"ZERO", "ONE"})
public void setValue(int value) {
this.value = value;
}
public static int ZERO = 0;
public static int ONE = 1;
}
public static class EVX {
private int value;
public int getValue() {
return this.value;
}
@BeanProperty(enumerationValues = {
"X.ZERO",
"X.ONE"})
public void setValue(int value) {
this.value = value;
}
}
public static interface X {
int ZERO = 0;
int ONE = 1;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2014, 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.
*
* 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.
*/
import java.awt.event.ActionListener;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.JavaBean;
/*
* @test
* @bug 4058433
* @summary Tests the JavaBean annotation
* @author Sergey Malenkov
*/
public class TestJavaBean {
public static void main(String[] args) throws Exception {
test(X.class);
test(D.class);
test(DP.class);
test(DES.class);
}
private static void test(Class<?> type) throws Exception {
System.out.println(type);
BeanInfo info = Introspector.getBeanInfo(type);
BeanDescriptor bd = info.getBeanDescriptor();
String description = bd.getShortDescription();
System.out.println("description = " + description);
int dp = info.getDefaultPropertyIndex();
System.out.println("property index = " + dp);
if (0 <= dp) {
String name = info.getPropertyDescriptors()[dp].getName();
System.out.println("property name = " + name);
}
int des = info.getDefaultEventIndex();
System.out.println("event set index = " + des);
if (0 <= des) {
String name = info.getPropertyDescriptors()[des].getName();
System.out.println("event set name = " + name);
}
if ((D.class == type) == bd.getName().equals(description)) {
throw new Error("unexpected description of the bean");
}
if ((DP.class == type) == (dp < 0)) {
throw new Error("unexpected index of the default property");
}
if ((DES.class == type) == (des < 0)) {
throw new Error("unexpected index of the default event set");
}
}
public static class X {
}
@JavaBean(description = "description")
public static class D {
}
@JavaBean(defaultProperty = "value")
public static class DP {
private int value;
public int getValue() {
return this.value;
}
public void setValue(int value) {
this.value = value;
}
}
@JavaBean(defaultEventSet = "action")
public static class DES {
public void addActionListener(ActionListener listener) {
}
public void removeActionListener(ActionListener listener) {
}
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2014, 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.
*
* 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.
*/
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.util.Objects;
import javax.swing.SwingContainer;
/*
* @test
* @bug 4058433
* @summary Tests the SwingContainer annotation
* @author Sergey Malenkov
*/
public class TestSwingContainer {
public static void main(String[] args) throws Exception {
test(X.class, null, null);
test(T.class, true, null);
test(D.class, true, "method");
test(F.class, false, null);
test(A.class, false, "method");
}
private static void test(Class<?> type, Object iC, Object cD) throws Exception {
System.out.println(type);
BeanInfo info = Introspector.getBeanInfo(type);
BeanDescriptor bd = info.getBeanDescriptor();
test(bd, "isContainer", iC);
test(bd, "containerDelegate", cD);
}
private static void test(BeanDescriptor bd, String name, Object expected) {
Object value = bd.getValue(name);
System.out.println(name + " = " + value);
if (!Objects.equals(value, expected)) {
throw new Error(name + ": expected = " + expected + "; actual = " + value);
}
}
public static class X {
}
@SwingContainer()
public static class T {
}
@SwingContainer(delegate = "method")
public static class D {
}
@SwingContainer(false)
public static class F {
}
@SwingContainer(value = false, delegate = "method")
public static class A {
}
}

View File

@ -27,6 +27,7 @@ import java.lang.reflect.Method;
* @bug 7084904
* @summary Compares reflection and bean introspection
* @author Sergey Malenkov
* @library ..
*/
public class Test7084904 {
public static void main(String[] args) throws Exception {

View File

@ -0,0 +1,249 @@
/*
* Copyright (c) 2014, 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.
*
* 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.
*/
import java.awt.Image;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.FeatureDescriptor;
import java.beans.IndexedPropertyDescriptor;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.ParameterDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* @test
* @bug 4058433
* @summary Generates BeanInfo for public classes in AWT, Accessibility, and Swing
* @author Sergey Malenkov
* @run main/manual Test4058433
*/
public class Test4058433 implements Comparator<Object> {
@Override
public int compare(Object one, Object two) {
if (one instanceof Method && two instanceof Method) {
Method oneMethod = (Method) one;
Method twoMethod = (Method) two;
int result = oneMethod.getName().compareTo(twoMethod.getName());
if (result != 0) {
return result;
}
}
if (one instanceof FeatureDescriptor && two instanceof FeatureDescriptor) {
FeatureDescriptor oneFD = (FeatureDescriptor) one;
FeatureDescriptor twoFD = (FeatureDescriptor) two;
int result = oneFD.getName().compareTo(twoFD.getName());
if (result != 0) {
return result;
}
}
return one.toString().compareTo(two.toString());
}
public static void main(String[] args) throws Exception {
String resource = ClassLoader.getSystemResource("java/lang/Object.class").toString();
Pattern pattern = Pattern.compile("jar:file:(.*)!.*");
Matcher matcher = pattern.matcher(resource);
matcher.matches();
resource = matcher.group(1);
TreeSet<Class<?>> types = new TreeSet<>(new Test4058433());
try (JarFile jarFile = new JarFile(resource.replaceAll("%20", " "))) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (name.startsWith("java/awt/") || name.startsWith("javax/accessibility/") || name.startsWith("javax/swing/")) {
if (name.endsWith(".class")) {
name = name.substring(0, name.indexOf(".")).replace('/', '.');
Class<?> type = Class.forName(name);
if (!type.isInterface() && !type.isEnum() && !type.isAnnotation() && !type.isAnonymousClass()) {
if (null == type.getDeclaringClass()) {
types.add(type);
}
}
}
}
}
}
System.out.println("found " + types.size() + " classes");
long time = -System.currentTimeMillis();
for (Class<?> type : types) {
System.out.println("========================================");
BeanInfo info = Introspector.getBeanInfo(type);
BeanDescriptor bd = info.getBeanDescriptor();
System.out.println(bd.getBeanClass());
print("customizer", bd.getCustomizerClass());
print(bd);
print("mono 16x16", info.getIcon(BeanInfo.ICON_MONO_16x16));
print("mono 32x32", info.getIcon(BeanInfo.ICON_MONO_32x32));
print("color 16x16", info.getIcon(BeanInfo.ICON_COLOR_16x16));
print("color 32x32", info.getIcon(BeanInfo.ICON_COLOR_32x32));
PropertyDescriptor[] pds = info.getPropertyDescriptors();
PropertyDescriptor dpd = getDefault(pds, info.getDefaultPropertyIndex());
System.out.println(pds.length + " property descriptors");
Arrays.sort(pds, new Test4058433());
for (PropertyDescriptor pd : pds) {
print(pd);
if (dpd == pd) {
System.out.println("default property");
}
print("bound", pd.isBound());
print("constrained", pd.isConstrained());
print("property editor", pd.getPropertyEditorClass());
print("property type", pd.getPropertyType());
print("read method", pd.getReadMethod());
print("write method", pd.getWriteMethod());
if (pd instanceof IndexedPropertyDescriptor) {
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
print("indexed property type", ipd.getIndexedPropertyType());
print("indexed read method", ipd.getIndexedReadMethod());
print("indexed write method", ipd.getIndexedWriteMethod());
}
}
EventSetDescriptor[] esds = info.getEventSetDescriptors();
EventSetDescriptor desd = getDefault(esds, info.getDefaultEventIndex());
System.out.println(esds.length + " event set descriptors");
Arrays.sort(esds, new Test4058433());
for (EventSetDescriptor esd : esds) {
print(esd);
if (desd == esd) {
System.out.println("default event set");
}
print("in default", esd.isInDefaultEventSet());
print("unicast", esd.isUnicast());
print("listener type", esd.getListenerType());
print("get listener method", esd.getGetListenerMethod());
print("add listener method", esd.getAddListenerMethod());
print("remove listener method", esd.getRemoveListenerMethod());
Method[] methods = esd.getListenerMethods();
Arrays.sort(methods, new Test4058433());
for (Method method : methods) {
print("listener method", method);
}
print(esd.getListenerMethodDescriptors());
}
print(info.getMethodDescriptors());
}
time += System.currentTimeMillis();
System.out.println("DONE IN " + time + " MS");
}
private static <T> T getDefault(T[] array, int index) {
return (index == -1) ? null : array[index];
}
private static void print(MethodDescriptor[] mds) {
System.out.println(mds.length + " method descriptors");
Arrays.sort(mds, new Test4058433());
for (MethodDescriptor md : mds) {
print(md);
print("method", md.getMethod());
ParameterDescriptor[] pds = md.getParameterDescriptors();
if (pds != null) {
System.out.println(pds.length + " parameter descriptors");
for (ParameterDescriptor pd : pds) {
print(pd);
}
}
}
}
private static void print(FeatureDescriptor descriptor) {
String name = descriptor.getName();
String display = descriptor.getDisplayName();
String description = descriptor.getShortDescription();
System.out.println("name: " + name);
if (!Objects.equals(name, display)) {
System.out.println("display name: " + display);
}
if (!Objects.equals(display, description)) {
System.out.println("description: " + description.trim());
}
print("expert", descriptor.isExpert());
print("hidden", descriptor.isHidden());
print("preferred", descriptor.isPreferred());
TreeMap<String,Object> map = new TreeMap<>();
Enumeration<String> enumeration = descriptor.attributeNames();
while (enumeration.hasMoreElements()) {
String id = enumeration.nextElement();
Object value = descriptor.getValue(id);
if (value.getClass().isArray()) {
TreeSet<String> set = new TreeSet<>();
int index = 0;
int length = Array.getLength(value);
while (index < length) {
set.add(Array.get(value, index++) + ", " +
Array.get(value, index++) + ", " +
Array.get(value, index++));
}
value = set.toString();
}
map.put(id, value);
}
for (Entry<String,Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
private static void print(String id, boolean flag) {
if (flag) {
System.out.println(id + " is set");
}
}
private static void print(String id, Class<?> type) {
if (type != null) {
System.out.println(id + ": " + type.getName());
}
}
private static void print(String id, Method method) {
if (method != null) {
System.out.println(id + ": " + method);
}
}
private static void print(String name, Image image) {
if (image != null) {
System.out.println(name + " icon is exist");
}
}
}