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 }