001 /* 002 * Copyright 2002-2004 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.commons.clazz; 017 018 import java.lang.ref.WeakReference; 019 import java.util.ArrayList; 020 import java.util.HashMap; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.apache.commons.clazz.common.ClazzElementSupport; 026 import org.apache.commons.clazz.common.ExtendedClazzLoaderFactory; 027 import org.apache.commons.clazz.common.StandardClazzLoaderFactory; 028 029 /** 030 * 031 * 032 * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a> 033 * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a> 034 * @version $Id: Clazz.java 155436 2005-02-26 13:17:48Z dirkv $ 035 */ 036 public abstract class Clazz extends ClazzElementSupport 037 implements ClazzElement 038 { 039 private ClazzLoader loader; 040 private String name; 041 private List listeners; 042 043 /** 044 * The name of the standard clazz model. The value of the constant is 045 * "Standard". 046 */ 047 public static final String STANDARD_CLAZZ_MODEL = 048 StandardClazzLoaderFactory.MODEL; 049 050 /** 051 * The name of the extended clazz model. The value of the constant is 052 * "Extended". 053 */ 054 public static final String EXTENDED_CLAZZ_MODEL = 055 ExtendedClazzLoaderFactory.MODEL; 056 057 private static String defaultClazzModel = EXTENDED_CLAZZ_MODEL; 058 private static ClazzLoader defaultClazzLoader; 059 private static Map clazzLoaderFactories = new HashMap(); 060 061 static { 062 addClazzLoaderFactory(StandardClazzLoaderFactory.FACTORY); 063 addClazzLoaderFactory(ExtendedClazzLoaderFactory.FACTORY); 064 } 065 066 /** 067 * Register a clazz loader factory, which manages ClazzLoaders, which manage 068 * Clazzes. 069 * 070 * @param clazzLoaderFactory 071 */ 072 public static void addClazzLoaderFactory(ClazzLoaderFactory factory) { 073 clazzLoaderFactories.put(factory.getClazzModel(), factory); 074 } 075 076 /** 077 * Returns a ClazzLoaderFactory registered for the supplied model. We can 078 * have multiple clazz loader factories implementing different models (e.g. 079 * Standard JavaBeans, Extended JavaBeans etc). 080 * 081 * @param model is the type of the model we need 082 * @return ClazzLoaderFactory 083 */ 084 public static ClazzLoaderFactory getClazzLoaderFactory(String model) { 085 return (ClazzLoaderFactory) clazzLoaderFactories.get(model); 086 } 087 088 /** 089 * Select the default clazz model. 090 * 091 * @param defaultClazzModel 092 */ 093 public static void setDefaultClazzModel(String defaultClazzModel) { 094 Clazz.defaultClazzModel = defaultClazzModel; 095 } 096 097 /** 098 * Returns the name of the default clazz model. 099 */ 100 public static String getDefaultClazzModel() { 101 return defaultClazzModel; 102 } 103 104 /** 105 * Returns a clazz loader for the specified model. The provided ClassLoader 106 * can be used by the ClazzLoader for reflection. 107 * 108 * @param model 109 * @param classLoader 110 * @return ClazzLoader 111 */ 112 public static ClazzLoader getClazzLoader( 113 String model, 114 ClassLoader classLoader) 115 { 116 if (classLoader == null) { 117 classLoader = Clazz.class.getClassLoader(); 118 } 119 return getClazzLoaderFactory(model).getClazzLoader(classLoader); 120 } 121 122 /** 123 * Returns the ClazzLoader for the default clazz model. 124 */ 125 public static ClazzLoader getDefaultClazzLoader(ClassLoader classLoader) { 126 return getClazzLoader(getDefaultClazzModel(), classLoader); 127 } 128 129 /** 130 * Uses the clazz loader for the specified model to obtain the Clazz for the 131 * supplied object. 132 */ 133 public static Clazz getClazz(Object instance, String model) { 134 if (instance == null) { 135 throw new NullPointerException(); 136 } 137 138 ClassLoader classLoader = instance.getClass().getClassLoader(); 139 Clazz clazz = getClazzLoader(model, classLoader).getClazz(instance); 140 if (clazz == null) { 141 throw new ClazzNotFoundException(instance.getClass().getName()); 142 } 143 return clazz; 144 } 145 146 /** 147 * Uses the default clazz loader to obtain the Clazz for the supplied 148 * object. 149 */ 150 public static Clazz getClazz(Object instance) { 151 return getClazz(instance, getDefaultClazzModel()); 152 } 153 154 /** 155 * Keep the constructor protected or private, we want Clazzes to 156 * be created by the ClazzLoaders only. 157 * 158 * @param loader is the owning model clazz loader 159 * @param name must be unique within the model 160 */ 161 protected Clazz(ClazzLoader modelClazzLoader, String name) { 162 this.loader = modelClazzLoader; 163 this.name = name; 164 } 165 166 /** 167 * Every Clazz belongs to one and only one ClazzLoader. Its name is unique 168 * within that loader. 169 */ 170 public ClazzLoader getClazzLoader() { 171 return loader; 172 } 173 174 /** 175 * Returns the name of the Clazz. It is ok for the same name to be present 176 * in different ClazzLoaders, except CachingClazzLoaders. 177 */ 178 public String getName() { 179 return name; 180 } 181 182 /** 183 * Gets the package name. 184 * 185 * @return the package name 186 */ 187 public String getPackageName() { 188 if (name == null) { 189 return null; 190 } 191 int index = name.lastIndexOf('.'); 192 if (index == -1) { 193 return ""; 194 } 195 196 return name.substring(0, index); 197 } 198 199 /** 200 * Gets the class name (without the package). 201 * 202 * @return the class name (without the package) 203 */ 204 public String getShortClassName() { 205 if (name == null) { 206 return null; 207 } 208 int index = name.lastIndexOf('.'); 209 if (index == -1) { 210 return name; 211 } 212 213 return name.substring(index + 1); 214 } 215 216 /** 217 * Returns the class of instances created by the <code>newInstance()</code> 218 * method. 219 */ 220 public abstract Class getInstanceClass(); 221 222 /** 223 * Returns the superclazz for this Clazz, or null if there is none. 224 */ 225 public abstract Clazz getSuperclazz(); 226 227 /** 228 * Returns true if the supplied clazz is either the same or a subclazz of 229 * this clazz. 230 */ 231 public boolean isAssignableFrom(Clazz clazz) { 232 if (clazz == this) { 233 return true; 234 } 235 Clazz superclazz = clazz.getSuperclazz(); 236 if (superclazz != null) { 237 return isAssignableFrom(superclazz); 238 } 239 return false; 240 } 241 242 /** 243 * Returns properties declared by this Clazz, not its superclazzes 244 */ 245 public abstract List getDeclaredProperties(); 246 247 /** 248 * Returns all properties for this clazz, including those declared 249 * by superclazzes. 250 */ 251 public abstract List getProperties(); 252 253 /** 254 * Returns the properties that match the Predicate. 255 */ 256 // public abstract List getProperties(Predicate predicate); 257 258 /** 259 * Returns a ClazzProperty for the given name 260 */ 261 public abstract ClazzProperty getProperty(String name); 262 263 /** 264 * Returns all Operations for this clazz. 265 */ 266 public abstract List getOperations(); 267 268 /** 269 * Returns all Operations declared by this clazz, not its superclazzes. 270 */ 271 public abstract List getDeclaredOperations(); 272 273 /** 274 * Returns the Operations that match the Predicate. 275 */ 276 // public abstract List getOperations(Predicate predicate); 277 278 /** 279 * Returns the Operation for the given signature. The signature should be 280 * formatted as follows: <code>"name(paramClazzName1,...)"</code> 281 */ 282 public abstract ClazzOperation getOperation(String signature); 283 284 /** 285 * Returns all InstanceFactories for this clazz. 286 */ 287 public abstract List getInstanceFactories(); 288 289 /** 290 * Returns the InstanceFactories that match the Predicate. 291 */ 292 // public abstract List getInstanceFactories(Predicate predicate); 293 294 /** 295 * Returns ClazzInstanceFactory for the given signature. The signature 296 * should be formatted as follows: <code>"(paramClazzName1,...)"</code>. You 297 * can pass <code>null</code> in place of <code>"()"</code>. 298 */ 299 public abstract ClazzInstanceFactory getInstanceFactory(String signature); 300 301 /** 302 * Creates a new instance of this Clazz using the InstanceFactory that takes 303 * no parameters. 304 */ 305 public Object newInstance() { 306 ClazzInstanceFactory factory = getInstanceFactory("()"); 307 if (factory == null) { 308 // @todo: define exception 309 throw new RuntimeException( 310 "No such instance factory " + getName() + " ()"); 311 } 312 return factory.newInstance(null); 313 } 314 315 /** 316 * Creates a new instance of this Clazz using the InstanceFactory 317 * with the specified signature. 318 */ 319 public Object newInstance(String signature, Object[] parameters) { 320 ClazzInstanceFactory factory = getInstanceFactory(signature); 321 if (factory == null) { 322 // @todo: define exception 323 throw new RuntimeException( 324 "No such instance factory " 325 + getName() 326 + (signature == null ? "" : " " + signature)); 327 } 328 return factory.newInstance(parameters); 329 } 330 331 332 //--- Notification mechanism - verbose, but what are you gonna do? 333 334 public void addClazzChangeListener(ClazzChangeListener listener) { 335 if (listeners == null) { 336 listeners = new ArrayList(); 337 } 338 339 // We don't want to prevent the subclasses from being garbage 340 // collection just because they are listening to their superclasses. 341 WeakReference reference = new WeakReference(listener); 342 listeners.add(reference); 343 } 344 345 public void removeClazzChangeListener(ClazzChangeListener listener) { 346 if (listeners == null) { 347 return; 348 } 349 for (Iterator iter = listeners.iterator(); iter.hasNext();) { 350 WeakReference reference = (WeakReference) iter.next(); 351 if (reference.get() == listener) { 352 iter.remove(); 353 break; 354 } 355 } 356 } 357 358 private static final WeakReference[] CLAZZ_CHANGE_LISTENER_ARRAY = 359 new WeakReference[0]; 360 361 private abstract static class Notifier { 362 void fire(Clazz clazz, Object parameter) { 363 if (clazz.listeners != null && clazz.listeners.size() != 0) { 364 WeakReference listenerArray[] = 365 (WeakReference[]) clazz.listeners.toArray( 366 CLAZZ_CHANGE_LISTENER_ARRAY); 367 for (int i = 0; i < listenerArray.length; i++) { 368 fire( 369 clazz, 370 (ClazzChangeListener) listenerArray[i].get(), 371 parameter); 372 } 373 } 374 } 375 376 abstract void fire( 377 Clazz clazz, ClazzChangeListener listener, Object parameter); 378 } 379 380 private static final Notifier PROPERTY_ADDED_NOTIFIER = new Notifier() { 381 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 382 listener.propertyAdded(clazz, (ClazzProperty) parameter); 383 } 384 }; 385 386 protected void firePropertyAdded(ClazzProperty property) { 387 PROPERTY_ADDED_NOTIFIER.fire(this, property); 388 } 389 390 private static final Notifier PROPERTY_REMOVED_NOTIFIER = new Notifier() { 391 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 392 listener.propertyRemoved(clazz, (ClazzProperty) parameter); 393 } 394 }; 395 396 protected void firePropertyRemoved(ClazzProperty property) { 397 PROPERTY_REMOVED_NOTIFIER.fire(this, property); 398 } 399 400 private static final Notifier OPERATION_ADDED_NOTIFIER = new Notifier() { 401 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 402 listener.operationAdded(clazz, (ClazzOperation) parameter); 403 } 404 }; 405 406 protected void fireOperationAdded(ClazzOperation operation) { 407 OPERATION_ADDED_NOTIFIER.fire(this, operation); 408 } 409 410 private static final Notifier OPERATION_REMOVED_NOTIFIER = new Notifier() { 411 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 412 listener.operationRemoved(clazz, (ClazzOperation) parameter); 413 } 414 }; 415 416 protected void fireOperationRemoved(ClazzOperation operation) { 417 OPERATION_REMOVED_NOTIFIER.fire(this, operation); 418 } 419 420 421 private static final Notifier FACTORY_ADDED_NOTIFIER = new Notifier() { 422 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 423 listener.instanceFactoryAdded( 424 clazz, 425 (ClazzInstanceFactory) parameter); 426 } 427 }; 428 429 protected void fireInstanceFactoryAdded(ClazzInstanceFactory factory) { 430 FACTORY_ADDED_NOTIFIER.fire(this, factory); 431 } 432 433 private static final Notifier FACTORY_REMOVED_NOTIFIER = new Notifier() { 434 void fire(Clazz clazz, ClazzChangeListener listener, Object parameter) { 435 listener.instanceFactoryRemoved( 436 clazz, 437 (ClazzInstanceFactory) parameter); 438 } 439 }; 440 441 protected void fireInstanceFactoryRemoved(ClazzInstanceFactory factory) { 442 FACTORY_REMOVED_NOTIFIER.fire(this, factory); 443 } 444 445 //--- End notification mechanism 446 447 448 /** 449 * Creates a signature string out of an operation or instance factory name 450 * and parameter types. 451 */ 452 public static String constructSignature(String name, Class[] arguments) { 453 StringBuffer buffer = new StringBuffer(); 454 if (name != null) { 455 buffer.append(name); 456 } 457 buffer.append('('); 458 if (arguments != null) { 459 for (int i = 0; i < arguments.length; i++) { 460 if (i != 0) { 461 buffer.append(','); 462 } 463 buffer.append(getCanonicalClassName(arguments[i])); 464 } 465 } 466 buffer.append(')'); 467 return buffer.toString(); 468 } 469 470 /** 471 * Creates a signature string out of an operation or instance factory name 472 * and parameter types. 473 */ 474 public static String constructSignature(String name, Clazz[] arguments) { 475 StringBuffer buffer = new StringBuffer(); 476 if (name != null) { 477 buffer.append(name); 478 } 479 buffer.append('('); 480 if (arguments != null) { 481 for (int i = 0; i < arguments.length; i++) { 482 if (i != 0) { 483 buffer.append(','); 484 } 485 buffer.append(arguments[i].getName()); 486 } 487 } 488 buffer.append(')'); 489 return buffer.toString(); 490 } 491 492 /** 493 * Produces a nice type name for a classes representing an array, e.g. "[I" 494 * is shown as "int[]". For non-array types returns the regular type name. 495 */ 496 public static String getCanonicalClassName(Class javaClass) { 497 /* 498 * This is basically a remake of java.lang.reflect.Field.getTypeName 499 * (Class), which cannot be used directly as it is declared with the 500 * default scope. 501 */ 502 if (javaClass.isArray()) { 503 return getCanonicalClassName(javaClass.getComponentType()) + "[]"; 504 } 505 return javaClass.getName(); 506 } 507 }