1 /*
   2  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.beans;
  27 
  28 import com.sun.beans.TypeResolver;
  29 
  30 import java.lang.ref.Reference;
  31 import java.lang.ref.WeakReference;
  32 import java.lang.ref.SoftReference;
  33 
  34 import java.lang.reflect.Method;
  35 
  36 import java.util.Enumeration;
  37 import java.util.Hashtable;
  38 import java.util.Map.Entry;
  39 
  40 /**
  41  * The FeatureDescriptor class is the common baseclass for PropertyDescriptor,
  42  * EventSetDescriptor, and MethodDescriptor, etc.
  43  * <p>
  44  * It supports some common information that can be set and retrieved for
  45  * any of the introspection descriptors.
  46  * <p>
  47  * In addition it provides an extension mechanism so that arbitrary
  48  * attribute/value pairs can be associated with a design feature.
  49  */
  50 
  51 public class FeatureDescriptor {
  52     private static final String TRANSIENT = "transient";
  53 
  54     private Reference<? extends Class<?>> classRef;
  55 
  56     /**
  57      * Constructs a <code>FeatureDescriptor</code>.
  58      */
  59     public FeatureDescriptor() {
  60     }
  61 
  62     /**
  63      * Gets the programmatic name of this feature.
  64      *
  65      * @return The programmatic name of the property/method/event
  66      */
  67     public String getName() {
  68         return name;
  69     }
  70 
  71     /**
  72      * Sets the programmatic name of this feature.
  73      *
  74      * @param name  The programmatic name of the property/method/event
  75      */
  76     public void setName(String name) {
  77         this.name = name;
  78     }
  79 
  80     /**
  81      * Gets the localized display name of this feature.
  82      *
  83      * @return The localized display name for the property/method/event.
  84      *  This defaults to the same as its programmatic name from getName.
  85      */
  86     public String getDisplayName() {
  87         if (displayName == null) {
  88             return getName();
  89         }
  90         return displayName;
  91     }
  92 
  93     /**
  94      * Sets the localized display name of this feature.
  95      *
  96      * @param displayName  The localized display name for the
  97      *          property/method/event.
  98      */
  99     public void setDisplayName(String displayName) {
 100         this.displayName = displayName;
 101     }
 102 
 103     /**
 104      * The "expert" flag is used to distinguish between those features that are
 105      * intended for expert users from those that are intended for normal users.
 106      *
 107      * @return True if this feature is intended for use by experts only.
 108      */
 109     public boolean isExpert() {
 110         return expert;
 111     }
 112 
 113     /**
 114      * The "expert" flag is used to distinguish between features that are
 115      * intended for expert users from those that are intended for normal users.
 116      *
 117      * @param expert True if this feature is intended for use by experts only.
 118      */
 119     public void setExpert(boolean expert) {
 120         this.expert = expert;
 121     }
 122 
 123     /**
 124      * The "hidden" flag is used to identify features that are intended only
 125      * for tool use, and which should not be exposed to humans.
 126      *
 127      * @return True if this feature should be hidden from human users.
 128      */
 129     public boolean isHidden() {
 130         return hidden;
 131     }
 132 
 133     /**
 134      * The "hidden" flag is used to identify features that are intended only
 135      * for tool use, and which should not be exposed to humans.
 136      *
 137      * @param hidden  True if this feature should be hidden from human users.
 138      */
 139     public void setHidden(boolean hidden) {
 140         this.hidden = hidden;
 141     }
 142 
 143     /**
 144      * The "preferred" flag is used to identify features that are particularly
 145      * important for presenting to humans.
 146      *
 147      * @return True if this feature should be preferentially shown to human users.
 148      */
 149     public boolean isPreferred() {
 150         return preferred;
 151     }
 152 
 153     /**
 154      * The "preferred" flag is used to identify features that are particularly
 155      * important for presenting to humans.
 156      *
 157      * @param preferred  True if this feature should be preferentially shown
 158      *                   to human users.
 159      */
 160     public void setPreferred(boolean preferred) {
 161         this.preferred = preferred;
 162     }
 163 
 164     /**
 165      * Gets the short description of this feature.
 166      *
 167      * @return  A localized short description associated with this
 168      *   property/method/event.  This defaults to be the display name.
 169      */
 170     public String getShortDescription() {
 171         if (shortDescription == null) {
 172             return getDisplayName();
 173         }
 174         return shortDescription;
 175     }
 176 
 177     /**
 178      * You can associate a short descriptive string with a feature.  Normally
 179      * these descriptive strings should be less than about 40 characters.
 180      * @param text  A (localized) short description to be associated with
 181      * this property/method/event.
 182      */
 183     public void setShortDescription(String text) {
 184         shortDescription = text;
 185     }
 186 
 187     /**
 188      * Associate a named attribute with this feature.
 189      *
 190      * @param attributeName  The locale-independent name of the attribute
 191      * @param value  The value.
 192      */
 193     public void setValue(String attributeName, Object value) {
 194         getTable().put(attributeName, value);
 195     }
 196 
 197     /**
 198      * Retrieve a named attribute with this feature.
 199      *
 200      * @param attributeName  The locale-independent name of the attribute
 201      * @return  The value of the attribute.  May be null if
 202      *     the attribute is unknown.
 203      */
 204     public Object getValue(String attributeName) {
 205         return (this.table != null)
 206                 ? this.table.get(attributeName)
 207                 : null;
 208     }
 209 
 210     /**
 211      * Gets an enumeration of the locale-independent names of this
 212      * feature.
 213      *
 214      * @return  An enumeration of the locale-independent names of any
 215      *    attributes that have been registered with setValue.
 216      */
 217     public Enumeration<String> attributeNames() {
 218         return getTable().keys();
 219     }
 220 
 221     /**
 222      * Package-private constructor,
 223      * Merge information from two FeatureDescriptors.
 224      * The merged hidden and expert flags are formed by or-ing the values.
 225      * In the event of other conflicts, the second argument (y) is
 226      * given priority over the first argument (x).
 227      *
 228      * @param x  The first (lower priority) MethodDescriptor
 229      * @param y  The second (higher priority) MethodDescriptor
 230      */
 231     FeatureDescriptor(FeatureDescriptor x, FeatureDescriptor y) {
 232         expert = x.expert | y.expert;
 233         hidden = x.hidden | y.hidden;
 234         preferred = x.preferred | y.preferred;
 235         name = y.name;
 236         shortDescription = x.shortDescription;
 237         if (y.shortDescription != null) {
 238             shortDescription = y.shortDescription;
 239         }
 240         displayName = x.displayName;
 241         if (y.displayName != null) {
 242             displayName = y.displayName;
 243         }
 244         classRef = x.classRef;
 245         if (y.classRef != null) {
 246             classRef = y.classRef;
 247         }
 248         addTable(x.table);
 249         addTable(y.table);
 250     }
 251 
 252     /*
 253      * Package-private dup constructor
 254      * This must isolate the new object from any changes to the old object.
 255      */
 256     FeatureDescriptor(FeatureDescriptor old) {
 257         expert = old.expert;
 258         hidden = old.hidden;
 259         preferred = old.preferred;
 260         name = old.name;
 261         shortDescription = old.shortDescription;
 262         displayName = old.displayName;
 263         classRef = old.classRef;
 264 
 265         addTable(old.table);
 266     }
 267 
 268     /**
 269      * Copies all values from the specified attribute table.
 270      * If some attribute is exist its value should be overridden.
 271      *
 272      * @param table  the attribute table with new values
 273      */
 274     private void addTable(Hashtable<String, Object> table) {
 275         if ((table != null) && !table.isEmpty()) {
 276             getTable().putAll(table);
 277         }
 278     }
 279 
 280     /**
 281      * Returns the initialized attribute table.
 282      *
 283      * @return the initialized attribute table
 284      */
 285     private Hashtable<String, Object> getTable() {
 286         if (this.table == null) {
 287             this.table = new Hashtable<>();
 288         }
 289         return this.table;
 290     }
 291 
 292     /**
 293      * Sets the "transient" attribute according to the annotation.
 294      * If the "transient" attribute is already set
 295      * it should not be changed.
 296      *
 297      * @param annotation  the annotation of the element of the feature
 298      */
 299     void setTransient(Transient annotation) {
 300         if ((annotation != null) && (null == getValue(TRANSIENT))) {
 301             setValue(TRANSIENT, annotation.value());
 302         }
 303     }
 304 
 305     /**
 306      * Indicates whether the feature is transient.
 307      *
 308      * @return {@code true} if the feature is transient,
 309      *         {@code false} otherwise
 310      */
 311     boolean isTransient() {
 312         Object value = getValue(TRANSIENT);
 313         return (value instanceof Boolean)
 314                 ? (Boolean) value
 315                 : false;
 316     }
 317 
 318     // Package private methods for recreating the weak/soft referent
 319 
 320     void setClass0(Class<?> cls) {
 321         this.classRef = getWeakReference(cls);
 322     }
 323 
 324     Class<?> getClass0() {
 325         return (this.classRef != null)
 326                 ? this.classRef.get()
 327                 : null;
 328     }
 329 
 330     /**
 331      * Creates a new soft reference that refers to the given object.
 332      *
 333      * @return a new soft reference or <code>null</code> if object is <code>null</code>
 334      *
 335      * @see SoftReference
 336      */
 337     static <T> Reference<T> getSoftReference(T object) {
 338         return (object != null)
 339                 ? new SoftReference<>(object)
 340                 : null;
 341     }
 342 
 343     /**
 344      * Creates a new weak reference that refers to the given object.
 345      *
 346      * @return a new weak reference or <code>null</code> if object is <code>null</code>
 347      *
 348      * @see WeakReference
 349      */
 350     static <T> Reference<T> getWeakReference(T object) {
 351         return (object != null)
 352                 ? new WeakReference<>(object)
 353                 : null;
 354     }
 355 
 356     /**
 357      * Resolves the return type of the method.
 358      *
 359      * @param base    the class that contains the method in the hierarchy
 360      * @param method  the object that represents the method
 361      * @return a class identifying the return type of the method
 362      *
 363      * @see Method#getGenericReturnType
 364      * @see Method#getReturnType
 365      */
 366     static Class<?> getReturnType(Class<?> base, Method method) {
 367         if (base == null) {
 368             base = method.getDeclaringClass();
 369         }
 370         return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericReturnType()));
 371     }
 372 
 373     /**
 374      * Resolves the parameter types of the method.
 375      *
 376      * @param base    the class that contains the method in the hierarchy
 377      * @param method  the object that represents the method
 378      * @return an array of classes identifying the parameter types of the method
 379      *
 380      * @see Method#getGenericParameterTypes
 381      * @see Method#getParameterTypes
 382      */
 383     static Class<?>[] getParameterTypes(Class<?> base, Method method) {
 384         if (base == null) {
 385             base = method.getDeclaringClass();
 386         }
 387         return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericParameterTypes()));
 388     }
 389 
 390     private boolean expert;
 391     private boolean hidden;
 392     private boolean preferred;
 393     private String shortDescription;
 394     private String name;
 395     private String displayName;
 396     private Hashtable<String, Object> table;
 397 
 398     /**
 399      * Returns a string representation of the object.
 400      *
 401      * @return a string representation of the object
 402      *
 403      * @since 1.7
 404      */
 405     public String toString() {
 406         StringBuilder sb = new StringBuilder(getClass().getName());
 407         sb.append("[name=").append(this.name);
 408         appendTo(sb, "displayName", this.displayName);
 409         appendTo(sb, "shortDescription", this.shortDescription);
 410         appendTo(sb, "preferred", this.preferred);
 411         appendTo(sb, "hidden", this.hidden);
 412         appendTo(sb, "expert", this.expert);
 413         if ((this.table != null) && !this.table.isEmpty()) {
 414             sb.append("; values={");
 415             for (Entry<String, Object> entry : this.table.entrySet()) {
 416                 sb.append(entry.getKey()).append("=").append(entry.getValue()).append("; ");
 417             }
 418             sb.setLength(sb.length() - 2);
 419             sb.append("}");
 420         }
 421         appendTo(sb);
 422         return sb.append("]").toString();
 423     }
 424 
 425     void appendTo(StringBuilder sb) {
 426     }
 427 
 428     static void appendTo(StringBuilder sb, String name, Reference<?> reference) {
 429         if (reference != null) {
 430             appendTo(sb, name, reference.get());
 431         }
 432     }
 433 
 434     static void appendTo(StringBuilder sb, String name, Object value) {
 435         if (value != null) {
 436             sb.append("; ").append(name).append("=").append(value);
 437         }
 438     }
 439 
 440     static void appendTo(StringBuilder sb, String name, boolean value) {
 441         if (value) {
 442             sb.append("; ").append(name);
 443         }
 444     }
 445 }