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  * @since 1.1
  51  */
  52 
  53 public class FeatureDescriptor {
  54     private static final String TRANSIENT = "transient";
  55 
  56     private Reference<? extends Class<?>> classRef;
  57 
  58     /**
  59      * Constructs a <code>FeatureDescriptor</code>.
  60      */
  61     public FeatureDescriptor() {
  62     }
  63 
  64     /**
  65      * Gets the programmatic name of this feature.
  66      *
  67      * @return The programmatic name of the property/method/event
  68      */
  69     public String getName() {
  70         return name;
  71     }
  72 
  73     /**
  74      * Sets the programmatic name of this feature.
  75      *
  76      * @param name  The programmatic name of the property/method/event
  77      */
  78     public void setName(String name) {
  79         this.name = name;
  80     }
  81 
  82     /**
  83      * Gets the localized display name of this feature.
  84      *
  85      * @return The localized display name for the property/method/event.
  86      *  This defaults to the same as its programmatic name from getName.
  87      */
  88     public String getDisplayName() {
  89         if (displayName == null) {
  90             return getName();
  91         }
  92         return displayName;
  93     }
  94 
  95     /**
  96      * Sets the localized display name of this feature.
  97      *
  98      * @param displayName  The localized display name for the
  99      *          property/method/event.
 100      */
 101     public void setDisplayName(String displayName) {
 102         this.displayName = displayName;
 103     }
 104 
 105     /**
 106      * The "expert" flag is used to distinguish between those features that are
 107      * intended for expert users from those that are intended for normal users.
 108      *
 109      * @return True if this feature is intended for use by experts only.
 110      */
 111     public boolean isExpert() {
 112         return expert;
 113     }
 114 
 115     /**
 116      * The "expert" flag is used to distinguish between features that are
 117      * intended for expert users from those that are intended for normal users.
 118      *
 119      * @param expert True if this feature is intended for use by experts only.
 120      */
 121     public void setExpert(boolean expert) {
 122         this.expert = expert;
 123     }
 124 
 125     /**
 126      * The "hidden" flag is used to identify features that are intended only
 127      * for tool use, and which should not be exposed to humans.
 128      *
 129      * @return True if this feature should be hidden from human users.
 130      */
 131     public boolean isHidden() {
 132         return hidden;
 133     }
 134 
 135     /**
 136      * The "hidden" flag is used to identify features that are intended only
 137      * for tool use, and which should not be exposed to humans.
 138      *
 139      * @param hidden  True if this feature should be hidden from human users.
 140      */
 141     public void setHidden(boolean hidden) {
 142         this.hidden = hidden;
 143     }
 144 
 145     /**
 146      * The "preferred" flag is used to identify features that are particularly
 147      * important for presenting to humans.
 148      *
 149      * @return True if this feature should be preferentially shown to human users.
 150      * @since 1.2
 151      */
 152     public boolean isPreferred() {
 153         return preferred;
 154     }
 155 
 156     /**
 157      * The "preferred" flag is used to identify features that are particularly
 158      * important for presenting to humans.
 159      *
 160      * @param preferred  True if this feature should be preferentially shown
 161      *                   to human users.
 162      * @since 1.2
 163      */
 164     public void setPreferred(boolean preferred) {
 165         this.preferred = preferred;
 166     }
 167 
 168     /**
 169      * Gets the short description of this feature.
 170      *
 171      * @return  A localized short description associated with this
 172      *   property/method/event.  This defaults to be the display name.
 173      */
 174     public String getShortDescription() {
 175         if (shortDescription == null) {
 176             return getDisplayName();
 177         }
 178         return shortDescription;
 179     }
 180 
 181     /**
 182      * You can associate a short descriptive string with a feature.  Normally
 183      * these descriptive strings should be less than about 40 characters.
 184      * @param text  A (localized) short description to be associated with
 185      * this property/method/event.
 186      */
 187     public void setShortDescription(String text) {
 188         shortDescription = text;
 189     }
 190 
 191     /**
 192      * Associate a named attribute with this feature.
 193      *
 194      * @param attributeName  The locale-independent name of the attribute
 195      * @param value  The value.
 196      */
 197     public void setValue(String attributeName, Object value) {
 198         getTable().put(attributeName, value);
 199     }
 200 
 201     /**
 202      * Retrieve a named attribute with this feature.
 203      *
 204      * @param attributeName  The locale-independent name of the attribute
 205      * @return  The value of the attribute.  May be null if
 206      *     the attribute is unknown.
 207      */
 208     public Object getValue(String attributeName) {
 209         return (this.table != null)
 210                 ? this.table.get(attributeName)
 211                 : null;
 212     }
 213 
 214     /**
 215      * Gets an enumeration of the locale-independent names of this
 216      * feature.
 217      *
 218      * @return  An enumeration of the locale-independent names of any
 219      *    attributes that have been registered with setValue.
 220      */
 221     public Enumeration<String> attributeNames() {
 222         return getTable().keys();
 223     }
 224 
 225     /**
 226      * Package-private constructor,
 227      * Merge information from two FeatureDescriptors.
 228      * The merged hidden and expert flags are formed by or-ing the values.
 229      * In the event of other conflicts, the second argument (y) is
 230      * given priority over the first argument (x).
 231      *
 232      * @param x  The first (lower priority) MethodDescriptor
 233      * @param y  The second (higher priority) MethodDescriptor
 234      */
 235     FeatureDescriptor(FeatureDescriptor x, FeatureDescriptor y) {
 236         expert = x.expert | y.expert;
 237         hidden = x.hidden | y.hidden;
 238         preferred = x.preferred | y.preferred;
 239         name = y.name;
 240         shortDescription = x.shortDescription;
 241         if (y.shortDescription != null) {
 242             shortDescription = y.shortDescription;
 243         }
 244         displayName = x.displayName;
 245         if (y.displayName != null) {
 246             displayName = y.displayName;
 247         }
 248         classRef = x.classRef;
 249         if (y.classRef != null) {
 250             classRef = y.classRef;
 251         }
 252         addTable(x.table);
 253         addTable(y.table);
 254     }
 255 
 256     /*
 257      * Package-private dup constructor
 258      * This must isolate the new object from any changes to the old object.
 259      */
 260     FeatureDescriptor(FeatureDescriptor old) {
 261         expert = old.expert;
 262         hidden = old.hidden;
 263         preferred = old.preferred;
 264         name = old.name;
 265         shortDescription = old.shortDescription;
 266         displayName = old.displayName;
 267         classRef = old.classRef;
 268 
 269         addTable(old.table);
 270     }
 271 
 272     /**
 273      * Copies all values from the specified attribute table.
 274      * If some attribute is exist its value should be overridden.
 275      *
 276      * @param table  the attribute table with new values
 277      */
 278     private void addTable(Hashtable<String, Object> table) {
 279         if ((table != null) && !table.isEmpty()) {
 280             getTable().putAll(table);
 281         }
 282     }
 283 
 284     /**
 285      * Returns the initialized attribute table.
 286      *
 287      * @return the initialized attribute table
 288      */
 289     private Hashtable<String, Object> getTable() {
 290         if (this.table == null) {
 291             this.table = new Hashtable<>();
 292         }
 293         return this.table;
 294     }
 295 
 296     /**
 297      * Sets the "transient" attribute according to the annotation.
 298      * If the "transient" attribute is already set
 299      * it should not be changed.
 300      *
 301      * @param annotation  the annotation of the element of the feature
 302      */
 303     void setTransient(Transient annotation) {
 304         if ((annotation != null) && (null == getValue(TRANSIENT))) {
 305             setValue(TRANSIENT, annotation.value());
 306         }
 307     }
 308 
 309     /**
 310      * Indicates whether the feature is transient.
 311      *
 312      * @return {@code true} if the feature is transient,
 313      *         {@code false} otherwise
 314      */
 315     boolean isTransient() {
 316         Object value = getValue(TRANSIENT);
 317         return (value instanceof Boolean)
 318                 ? (Boolean) value
 319                 : false;
 320     }
 321 
 322     // Package private methods for recreating the weak/soft referent
 323 
 324     void setClass0(Class<?> cls) {
 325         this.classRef = getWeakReference(cls);
 326     }
 327 
 328     Class<?> getClass0() {
 329         return (this.classRef != null)
 330                 ? this.classRef.get()
 331                 : null;
 332     }
 333 
 334     /**
 335      * Creates a new soft reference that refers to the given object.
 336      *
 337      * @return a new soft reference or <code>null</code> if object is <code>null</code>
 338      *
 339      * @see SoftReference
 340      */
 341     static <T> Reference<T> getSoftReference(T object) {
 342         return (object != null)
 343                 ? new SoftReference<>(object)
 344                 : null;
 345     }
 346 
 347     /**
 348      * Creates a new weak reference that refers to the given object.
 349      *
 350      * @return a new weak reference or <code>null</code> if object is <code>null</code>
 351      *
 352      * @see WeakReference
 353      */
 354     static <T> Reference<T> getWeakReference(T object) {
 355         return (object != null)
 356                 ? new WeakReference<>(object)
 357                 : null;
 358     }
 359 
 360     /**
 361      * Resolves the return type of the method.
 362      *
 363      * @param base    the class that contains the method in the hierarchy
 364      * @param method  the object that represents the method
 365      * @return a class identifying the return type of the method
 366      *
 367      * @see Method#getGenericReturnType
 368      * @see Method#getReturnType
 369      */
 370     static Class<?> getReturnType(Class<?> base, Method method) {
 371         if (base == null) {
 372             base = method.getDeclaringClass();
 373         }
 374         return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericReturnType()));
 375     }
 376 
 377     /**
 378      * Resolves the parameter types of the method.
 379      *
 380      * @param base    the class that contains the method in the hierarchy
 381      * @param method  the object that represents the method
 382      * @return an array of classes identifying the parameter types of the method
 383      *
 384      * @see Method#getGenericParameterTypes
 385      * @see Method#getParameterTypes
 386      */
 387     static Class<?>[] getParameterTypes(Class<?> base, Method method) {
 388         if (base == null) {
 389             base = method.getDeclaringClass();
 390         }
 391         return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericParameterTypes()));
 392     }
 393 
 394     private boolean expert;
 395     private boolean hidden;
 396     private boolean preferred;
 397     private String shortDescription;
 398     private String name;
 399     private String displayName;
 400     private Hashtable<String, Object> table;
 401 
 402     /**
 403      * Returns a string representation of the object.
 404      *
 405      * @return a string representation of the object
 406      *
 407      * @since 1.7
 408      */
 409     public String toString() {
 410         StringBuilder sb = new StringBuilder(getClass().getName());
 411         sb.append("[name=").append(this.name);
 412         appendTo(sb, "displayName", this.displayName);
 413         appendTo(sb, "shortDescription", this.shortDescription);
 414         appendTo(sb, "preferred", this.preferred);
 415         appendTo(sb, "hidden", this.hidden);
 416         appendTo(sb, "expert", this.expert);
 417         if ((this.table != null) && !this.table.isEmpty()) {
 418             sb.append("; values={");
 419             for (Entry<String, Object> entry : this.table.entrySet()) {
 420                 sb.append(entry.getKey()).append("=").append(entry.getValue()).append("; ");
 421             }
 422             sb.setLength(sb.length() - 2);
 423             sb.append("}");
 424         }
 425         appendTo(sb);
 426         return sb.append("]").toString();
 427     }
 428 
 429     void appendTo(StringBuilder sb) {
 430     }
 431 
 432     static void appendTo(StringBuilder sb, String name, Reference<?> reference) {
 433         if (reference != null) {
 434             appendTo(sb, name, reference.get());
 435         }
 436     }
 437 
 438     static void appendTo(StringBuilder sb, String name, Object value) {
 439         if (value != null) {
 440             sb.append("; ").append(name).append("=").append(value);
 441         }
 442     }
 443 
 444     static void appendTo(StringBuilder sb, String name, boolean value) {
 445         if (value) {
 446             sb.append("; ").append(name);
 447         }
 448     }
 449 }