/* |
|
* Copyright (c) 1996, 2011, 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 com.sun.beans.TypeResolver; |
|
import java.lang.ref.Reference; |
|
import java.lang.ref.WeakReference; |
|
import java.lang.ref.SoftReference; |
|
import java.lang.reflect.Method; |
|
import java.util.Enumeration; |
|
import java.util.Hashtable; |
|
import java.util.Map.Entry; |
|
/** |
|
* The FeatureDescriptor class is the common baseclass for PropertyDescriptor, |
|
* EventSetDescriptor, and MethodDescriptor, etc. |
|
* <p> |
|
* It supports some common information that can be set and retrieved for |
|
* any of the introspection descriptors. |
|
* <p> |
|
* In addition it provides an extension mechanism so that arbitrary |
|
* attribute/value pairs can be associated with a design feature. |
|
*/ |
|
public class FeatureDescriptor { |
|
private static final String TRANSIENT = "transient"; |
|
private Reference<? extends Class<?>> classRef; |
|
/** |
|
* Constructs a <code>FeatureDescriptor</code>. |
|
*/ |
|
public FeatureDescriptor() { |
|
} |
|
/** |
|
* Gets the programmatic name of this feature. |
|
* |
|
* @return The programmatic name of the property/method/event |
|
*/ |
|
public String getName() { |
|
return name; |
|
} |
|
/** |
|
* Sets the programmatic name of this feature. |
|
* |
|
* @param name The programmatic name of the property/method/event |
|
*/ |
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
/** |
|
* Gets the localized display name of this feature. |
|
* |
|
* @return The localized display name for the property/method/event. |
|
* This defaults to the same as its programmatic name from getName. |
|
*/ |
|
public String getDisplayName() { |
|
if (displayName == null) { |
|
return getName(); |
|
} |
|
return displayName; |
|
} |
|
/** |
|
* Sets the localized display name of this feature. |
|
* |
|
* @param displayName The localized display name for the |
|
* property/method/event. |
|
*/ |
|
public void setDisplayName(String displayName) { |
|
this.displayName = displayName; |
|
} |
|
/** |
|
* The "expert" flag is used to distinguish between those features that are |
|
* intended for expert users from those that are intended for normal users. |
|
* |
|
* @return True if this feature is intended for use by experts only. |
|
*/ |
|
public boolean isExpert() { |
|
return expert; |
|
} |
|
/** |
|
* The "expert" flag is used to distinguish between features that are |
|
* intended for expert users from those that are intended for normal users. |
|
* |
|
* @param expert True if this feature is intended for use by experts only. |
|
*/ |
|
public void setExpert(boolean expert) { |
|
this.expert = expert; |
|
} |
|
/** |
|
* The "hidden" flag is used to identify features that are intended only |
|
* for tool use, and which should not be exposed to humans. |
|
* |
|
* @return True if this feature should be hidden from human users. |
|
*/ |
|
public boolean isHidden() { |
|
return hidden; |
|
} |
|
/** |
|
* The "hidden" flag is used to identify features that are intended only |
|
* for tool use, and which should not be exposed to humans. |
|
* |
|
* @param hidden True if this feature should be hidden from human users. |
|
*/ |
|
public void setHidden(boolean hidden) { |
|
this.hidden = hidden; |
|
} |
|
/** |
|
* The "preferred" flag is used to identify features that are particularly |
|
* important for presenting to humans. |
|
* |
|
* @return True if this feature should be preferentially shown to human users. |
|
*/ |
|
public boolean isPreferred() { |
|
return preferred; |
|
} |
|
/** |
|
* The "preferred" flag is used to identify features that are particularly |
|
* important for presenting to humans. |
|
* |
|
* @param preferred True if this feature should be preferentially shown |
|
* to human users. |
|
*/ |
|
public void setPreferred(boolean preferred) { |
|
this.preferred = preferred; |
|
} |
|
/** |
|
* Gets the short description of this feature. |
|
* |
|
* @return A localized short description associated with this |
|
* property/method/event. This defaults to be the display name. |
|
*/ |
|
public String getShortDescription() { |
|
if (shortDescription == null) { |
|
return getDisplayName(); |
|
} |
|
return shortDescription; |
|
} |
|
/** |
|
* You can associate a short descriptive string with a feature. Normally |
|
* these descriptive strings should be less than about 40 characters. |
|
* @param text A (localized) short description to be associated with |
|
* this property/method/event. |
|
*/ |
|
public void setShortDescription(String text) { |
|
shortDescription = text; |
|
} |
|
/** |
|
* Associate a named attribute with this feature. |
|
* |
|
* @param attributeName The locale-independent name of the attribute |
|
* @param value The value. |
|
*/ |
|
public void setValue(String attributeName, Object value) { |
|
getTable().put(attributeName, value); |
|
} |
|
/** |
|
* Retrieve a named attribute with this feature. |
|
* |
|
* @param attributeName The locale-independent name of the attribute |
|
* @return The value of the attribute. May be null if |
|
* the attribute is unknown. |
|
*/ |
|
public Object getValue(String attributeName) { |
|
return (this.table != null) |
|
? this.table.get(attributeName) |
|
: null; |
|
} |
|
/** |
|
* Gets an enumeration of the locale-independent names of this |
|
* feature. |
|
* |
|
* @return An enumeration of the locale-independent names of any |
|
* attributes that have been registered with setValue. |
|
*/ |
|
public Enumeration<String> attributeNames() { |
|
return getTable().keys(); |
|
} |
|
/** |
|
* Package-private constructor, |
|
* Merge information from two FeatureDescriptors. |
|
* The merged hidden and expert flags are formed by or-ing the values. |
|
* In the event of other conflicts, the second argument (y) is |
|
* given priority over the first argument (x). |
|
* |
|
* @param x The first (lower priority) MethodDescriptor |
|
* @param y The second (higher priority) MethodDescriptor |
|
*/ |
|
FeatureDescriptor(FeatureDescriptor x, FeatureDescriptor y) { |
|
expert = x.expert | y.expert; |
|
hidden = x.hidden | y.hidden; |
|
preferred = x.preferred | y.preferred; |
|
name = y.name; |
|
shortDescription = x.shortDescription; |
|
if (y.shortDescription != null) { |
|
shortDescription = y.shortDescription; |
|
} |
|
displayName = x.displayName; |
|
if (y.displayName != null) { |
|
displayName = y.displayName; |
|
} |
|
classRef = x.classRef; |
|
if (y.classRef != null) { |
|
classRef = y.classRef; |
|
} |
|
addTable(x.table); |
|
addTable(y.table); |
|
} |
|
/* |
|
* Package-private dup constructor |
|
* This must isolate the new object from any changes to the old object. |
|
*/ |
|
FeatureDescriptor(FeatureDescriptor old) { |
|
expert = old.expert; |
|
hidden = old.hidden; |
|
preferred = old.preferred; |
|
name = old.name; |
|
shortDescription = old.shortDescription; |
|
displayName = old.displayName; |
|
classRef = old.classRef; |
|
addTable(old.table); |
|
} |
|
/** |
|
* Copies all values from the specified attribute table. |
|
* If some attribute is exist its value should be overridden. |
|
* |
|
* @param table the attribute table with new values |
|
*/ |
|
private void addTable(Hashtable<String, Object> table) { |
|
if ((table != null) && !table.isEmpty()) { |
|
getTable().putAll(table); |
|
} |
|
} |
|
/** |
|
* Returns the initialized attribute table. |
|
* |
|
* @return the initialized attribute table |
|
*/ |
|
private Hashtable<String, Object> getTable() { |
|
if (this.table == null) { |
|
this.table = new Hashtable<>(); |
|
} |
|
return this.table; |
|
} |
|
/** |
|
* Sets the "transient" attribute according to the annotation. |
|
* If the "transient" attribute is already set |
|
* it should not be changed. |
|
* |
|
* @param annotation the annotation of the element of the feature |
|
*/ |
|
void setTransient(Transient annotation) { |
|
if ((annotation != null) && (null == getValue(TRANSIENT))) { |
|
setValue(TRANSIENT, annotation.value()); |
|
} |
|
} |
|
/** |
|
* Indicates whether the feature is transient. |
|
* |
|
* @return {@code true} if the feature is transient, |
|
* {@code false} otherwise |
|
*/ |
|
boolean isTransient() { |
|
Object value = getValue(TRANSIENT); |
|
return (value instanceof Boolean) |
|
? (Boolean) value |
|
: false; |
|
} |
|
// Package private methods for recreating the weak/soft referent |
|
void setClass0(Class<?> cls) { |
|
this.classRef = getWeakReference(cls); |
|
} |
|
Class<?> getClass0() { |
|
return (this.classRef != null) |
|
? this.classRef.get() |
|
: null; |
|
} |
|
/** |
|
* Creates a new soft reference that refers to the given object. |
|
* |
|
* @return a new soft reference or <code>null</code> if object is <code>null</code> |
|
* |
|
* @see SoftReference |
|
*/ |
|
static <T> Reference<T> getSoftReference(T object) { |
|
return (object != null) |
|
? new SoftReference<>(object) |
|
: null; |
|
} |
|
/** |
|
* Creates a new weak reference that refers to the given object. |
|
* |
|
* @return a new weak reference or <code>null</code> if object is <code>null</code> |
|
* |
|
* @see WeakReference |
|
*/ |
|
static <T> Reference<T> getWeakReference(T object) { |
|
return (object != null) |
|
? new WeakReference<>(object) |
|
: null; |
|
} |
|
/** |
|
* Resolves the return type of the method. |
|
* |
|
* @param base the class that contains the method in the hierarchy |
|
* @param method the object that represents the method |
|
* @return a class identifying the return type of the method |
|
* |
|
* @see Method#getGenericReturnType |
|
* @see Method#getReturnType |
|
*/ |
|
static Class<?> getReturnType(Class<?> base, Method method) { |
|
if (base == null) { |
|
base = method.getDeclaringClass(); |
|
} |
|
return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericReturnType())); |
|
} |
|
/** |
|
* Resolves the parameter types of the method. |
|
* |
|
* @param base the class that contains the method in the hierarchy |
|
* @param method the object that represents the method |
|
* @return an array of classes identifying the parameter types of the method |
|
* |
|
* @see Method#getGenericParameterTypes |
|
* @see Method#getParameterTypes |
|
*/ |
|
static Class<?>[] getParameterTypes(Class<?> base, Method method) { |
|
if (base == null) { |
|
base = method.getDeclaringClass(); |
|
} |
|
return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericParameterTypes())); |
|
} |
|
private boolean expert; |
|
private boolean hidden; |
|
private boolean preferred; |
|
private String shortDescription; |
|
private String name; |
|
private String displayName; |
|
private Hashtable<String, Object> table; |
|
/** |
|
* Returns a string representation of the object. |
|
* |
|
* @return a string representation of the object |
|
* |
|
* @since 1.7 |
|
*/ |
|
public String toString() { |
|
StringBuilder sb = new StringBuilder(getClass().getName()); |
|
sb.append("[name=").append(this.name); |
|
appendTo(sb, "displayName", this.displayName); |
|
appendTo(sb, "shortDescription", this.shortDescription); |
|
appendTo(sb, "preferred", this.preferred); |
|
appendTo(sb, "hidden", this.hidden); |
|
appendTo(sb, "expert", this.expert); |
|
if ((this.table != null) && !this.table.isEmpty()) { |
|
sb.append("; values={"); |
|
for (Entry<String, Object> entry : this.table.entrySet()) { |
|
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("; "); |
|
} |
|
sb.setLength(sb.length() - 2); |
|
sb.append("}"); |
|
} |
|
appendTo(sb); |
|
return sb.append("]").toString(); |
|
} |
|
void appendTo(StringBuilder sb) { |
|
} |
|
static void appendTo(StringBuilder sb, String name, Reference<?> reference) { |
|
if (reference != null) { |
|
appendTo(sb, name, reference.get()); |
|
} |
|
} |
|
static void appendTo(StringBuilder sb, String name, Object value) { |
|
if (value != null) { |
|
sb.append("; ").append(name).append("=").append(value); |
|
} |
|
} |
|
static void appendTo(StringBuilder sb, String name, boolean value) { |
|
if (value) { |
|
sb.append("; ").append(name); |
|
} |
|
} |
|
} |