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.bean; 017 018 import java.lang.reflect.Constructor; 019 import java.util.ArrayList; 020 import java.util.Collections; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 028 import org.apache.commons.clazz.Clazz; 029 import org.apache.commons.clazz.ClazzChangeListener; 030 import org.apache.commons.clazz.ClazzInstanceFactory; 031 import org.apache.commons.clazz.ClazzLoader; 032 import org.apache.commons.clazz.ClazzOperation; 033 import org.apache.commons.clazz.ClazzProperty; 034 035 /** 036 * Dynamically constructed Clazz. BeanClazzes are created by invoking {@link 037 * ClazzLoader#defineClazz(String, Class, Class) clazzLoader.defineClazz (name, 038 * clazzClass, instanceClass)}. 039 * 040 * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a> 041 * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a> 042 * @version $Id: BeanClazz.java 155436 2005-02-26 13:17:48Z dirkv $ 043 */ 044 public class BeanClazz extends Clazz { 045 private Clazz superClazz; 046 private List declaredProperties = new ArrayList(); 047 private List properties = new ArrayList(); 048 private Map propertyMap = new HashMap(); 049 private List declaredOperations = new ArrayList(); 050 private List operations = new ArrayList(); 051 private Map operationMap = new HashMap(); 052 private List instanceFactories; 053 private Map instanceFactoryMap = new HashMap(); 054 private Class instanceClass; 055 private List subclasses = new ArrayList(); 056 057 /** 058 * Constructor for BeanClazz. 059 * @param loader 060 * @param name 061 * @param instanceClass 062 */ 063 public BeanClazz(ClazzLoader loader, String name, Class instanceClass) { 064 super(loader, name); 065 this.instanceClass = instanceClass; 066 } 067 068 public Class getInstanceClass() { 069 if (instanceClass != null) { 070 return instanceClass; 071 } 072 073 if (superClazz instanceof BeanClazz) { 074 return ((BeanClazz) superClazz).getInstanceClass(); 075 } 076 077 return BasicBean.class; 078 } 079 080 /** 081 * @see org.apache.commons.clazz.Clazz#getSuperclazz() 082 */ 083 public Clazz getSuperclazz() { 084 return superClazz; 085 } 086 087 public void setSuperclazz(Clazz clazz) { 088 if (superClazz != null) { 089 superClazz.removeClazzChangeListener(listener); 090 } 091 superClazz = clazz; 092 if (clazz != null) { 093 superClazz.addClazzChangeListener(listener); 094 } 095 refreshAllCaches(); 096 } 097 098 protected void refreshAllCaches() { 099 refreshPropertyCache(); 100 refreshOperationCache(); 101 } 102 103 protected void refreshPropertyCache() { 104 properties = new ArrayList(); 105 propertyMap = new HashMap(); 106 107 Set propertyNames = new HashSet(); 108 if (superClazz != null) { 109 List superProperties = superClazz.getProperties(); 110 properties.addAll(superProperties); 111 for (int i = 0; i < superProperties.size(); i++) { 112 ClazzProperty superProperty = 113 (ClazzProperty) superProperties.get(i); 114 propertyNames.add(superProperty.getName()); 115 } 116 } 117 for (int i = 0; i < declaredProperties.size(); i++) { 118 ClazzProperty property = (ClazzProperty) declaredProperties.get(i); 119 String name = property.getName(); 120 if (!propertyNames.contains(name)) { 121 properties.add(property); 122 } 123 } 124 125 for (Iterator iter = properties.iterator(); iter.hasNext();) { 126 ClazzProperty property = (ClazzProperty) iter.next(); 127 propertyMap.put(property.getName(), property); 128 } 129 } 130 131 protected void refreshOperationCache() { 132 List operations = new ArrayList(); 133 Set signatures = new HashSet(); 134 if (superClazz != null) { 135 List superOperations = superClazz.getOperations(); 136 operations.addAll(superOperations); 137 for (int i = 0; i < superOperations.size(); i++) { 138 ClazzOperation superOperation = 139 (ClazzOperation) superOperations.get(i); 140 signatures.add(superOperation.getSignature()); 141 } 142 } 143 for (int i = 0; i < declaredOperations.size(); i++) { 144 ClazzOperation operation = 145 (ClazzOperation) declaredOperations.get(i); 146 String signature = operation.getSignature(); 147 if (!signatures.contains(signature)) { 148 operations.add(operation); 149 } 150 } 151 } 152 153 /** 154 * @see org.apache.commons.clazz.Clazz#getDeclaredProperties() 155 */ 156 public List getDeclaredProperties() { 157 return Collections.unmodifiableList(declaredProperties); 158 } 159 160 public void addDeclaredProperty(ClazzProperty property) { 161 if (property.getDeclaringClazz() != this) { 162 throw new BeanClazzConfigurationException( 163 "Property belongs to a different clazz: " 164 + property.getDeclaringClazz().getName()); 165 } 166 167 ClazzProperty oldProperty = 168 (ClazzProperty) propertyMap.get(property.getName()); 169 if (oldProperty != null) { 170 removeDeclaredProperty(oldProperty); 171 } 172 173 declaredProperties.add(property); 174 175 addProperty(property); 176 } 177 178 /** 179 * Called indirectly when declared properties are manipulated. 180 */ 181 protected void addProperty(ClazzProperty property) { 182 properties.add(property); 183 propertyMap.put(property.getName(), property); 184 firePropertyAdded(property); 185 } 186 187 public void removeDeclaredProperty(ClazzProperty property) { 188 String name = property.getName(); 189 property = (ClazzProperty) propertyMap.get(name); 190 if (property != null) { 191 declaredProperties.remove(property); 192 removeProperty(property); 193 } 194 } 195 196 /** 197 * Called indirectly when declared properties are manipulated. 198 */ 199 protected void removeProperty(ClazzProperty property) { 200 String name = property.getName(); 201 properties.remove(property); 202 propertyMap.remove(name); 203 firePropertyRemoved(property); 204 205 // By deleting this declared property, we may have exposed 206 // an inherited one 207 if (superClazz != null) { 208 property = superClazz.getProperty(name); 209 if (property != null) { 210 addProperty(property); 211 } 212 } 213 } 214 215 /** 216 * @see org.apache.commons.clazz.Clazz#getProperties() 217 */ 218 public List getProperties() { 219 return properties; 220 } 221 222 /** 223 * @see org.apache.commons.clazz.Clazz#getProperty(java.lang.String) 224 */ 225 public ClazzProperty getProperty(String name) { 226 return (ClazzProperty) propertyMap.get(name); 227 } 228 229 /** 230 * @see org.apache.commons.clazz.Clazz#getDeclaredOperations() 231 */ 232 public List getDeclaredOperations() { 233 return Collections.unmodifiableList(declaredOperations); 234 } 235 236 public void addDeclaredOperation(ClazzOperation operation) { 237 if (operation.getDeclaringClazz() != this) { 238 throw new BeanClazzConfigurationException( 239 "Operation belongs to a different clazz: " 240 + operation.getDeclaringClazz().getName()); 241 } 242 243 ClazzOperation oldOperation = 244 (ClazzOperation) operationMap.get(operation.getSignature()); 245 if (oldOperation != null) { 246 removeDeclaredOperation(oldOperation); 247 } 248 249 declaredOperations.add(operation); 250 251 addOperation(operation); 252 } 253 254 /** 255 * Called indirectly when declared operations are manipulated. 256 */ 257 protected void addOperation(ClazzOperation operation) { 258 operations.add(operation); 259 operationMap.put(operation.getSignature(), operation); 260 fireOperationAdded(operation); 261 } 262 263 public void removeDeclaredOperation(ClazzOperation operation) { 264 String signature = operation.getSignature(); 265 operation = (ClazzOperation) operationMap.get(signature); 266 if (operation != null) { 267 declaredOperations.remove(operation); 268 removeOperation(operation); 269 } 270 } 271 272 /** 273 * Called indirectly when declared operations are manipulated. 274 */ 275 protected void removeOperation(ClazzOperation operation) { 276 String signature = operation.getSignature(); 277 operations.remove(operation); 278 operationMap.remove(signature); 279 fireOperationRemoved(operation); 280 281 // By deleting this declared operation, we may have exposed 282 // an inherited one 283 if (superClazz != null) { 284 operation = superClazz.getOperation(signature); 285 if (operation != null) { 286 addOperation(operation); 287 } 288 } 289 } 290 291 /** 292 * @see org.apache.commons.clazz.Clazz#getOperations() 293 */ 294 public List getOperations() { 295 return operations; 296 } 297 298 /** 299 * @see org.apache.commons.clazz.Clazz#getOperation(java.lang.String) 300 */ 301 public ClazzOperation getOperation(String signature) { 302 return (ClazzOperation) operationMap.get(signature); 303 } 304 305 /** 306 * @see org.apache.commons.clazz.Clazz#getInstanceFactories() 307 */ 308 public List getInstanceFactories() { 309 if (instanceFactories == null) { 310 introspectInstanceFactories(); 311 } 312 return Collections.unmodifiableList(instanceFactories); 313 } 314 315 /** 316 * @see org.apache.commons.clazz.Clazz#getInstanceFactory(java.lang.String) 317 */ 318 public ClazzInstanceFactory getInstanceFactory(String signature) { 319 if (instanceFactories == null) { 320 introspectInstanceFactories(); 321 } 322 323 return (ClazzInstanceFactory) instanceFactoryMap.get(signature); 324 } 325 326 public void addInstanceFactory(ClazzInstanceFactory factory) { 327 if (factory.getDeclaringClazz() != this) { 328 throw new BeanClazzConfigurationException( 329 "Instance factory belongs to a different clazz: " 330 + factory.getDeclaringClazz().getName()); 331 } 332 333 if (instanceFactories == null) { 334 introspectInstanceFactories(); 335 } 336 ClazzInstanceFactory oldFactory = 337 (ClazzInstanceFactory) instanceFactoryMap.get( 338 factory.getSignature()); 339 if (oldFactory != null) { 340 removeInstanceFactory(oldFactory); 341 } 342 instanceFactories.add(factory); 343 instanceFactoryMap.put(factory.getSignature(), factory); 344 fireInstanceFactoryAdded(factory); 345 } 346 347 public void removeInstanceFactory(ClazzInstanceFactory factory) { 348 if (instanceFactories.remove(factory)) { 349 instanceFactoryMap.remove(factory.getSignature()); 350 fireInstanceFactoryRemoved(factory); 351 } 352 } 353 354 /** 355 * Finds all constructors of the <code>instanceClass</code> that have at 356 * least one argument and the first argument is of type Clazz. Wraps 357 * each of those constructors into 358 * a {@link BeanClazzConstructorInstanceFactory 359 * BeanClazzConstructorInstanceFactory} and registers it as an 360 * InstanceFactory for this clazz. 361 */ 362 private void introspectInstanceFactories() { 363 instanceFactories = new ArrayList(); 364 Class instanceClass = getInstanceClass(); 365 Constructor constructors[] = instanceClass.getConstructors(); 366 for (int i = 0; i < constructors.length; i++) { 367 Class[] parameterTypes = constructors[i].getParameterTypes(); 368 if (parameterTypes != null 369 && parameterTypes.length >= 1 370 && Clazz.class.isAssignableFrom(parameterTypes[0])) { 371 addInstanceFactory( 372 new BeanClazzConstructorInstanceFactory( 373 this, 374 constructors[i])); 375 } 376 } 377 } 378 379 private ClazzChangeListener listener = new ClazzChangeListener() { 380 public void propertyAdded(Clazz clazz, ClazzProperty property) { 381 if (propertyMap.get(property.getName()) == null) { 382 addProperty(property); 383 } 384 } 385 386 public void propertyRemoved(Clazz clazz, ClazzProperty property) { 387 ClazzProperty declared = 388 (ClazzProperty) propertyMap.get(property.getName()); 389 if (declared != null && declared.equals(property)) { 390 removeProperty(property); 391 } 392 } 393 394 public void operationAdded(Clazz clazz, ClazzOperation operation) { 395 if (operationMap.get(operation.getSignature()) == null) { 396 addOperation(operation); 397 } 398 } 399 400 public void operationRemoved(Clazz clazz, ClazzOperation operation) { 401 ClazzOperation declared = 402 (ClazzOperation) operationMap.get(operation.getSignature()); 403 if (declared != null && declared.equals(operation)) { 404 removeOperation(operation); 405 } 406 } 407 408 public void instanceFactoryAdded( 409 Clazz clazz, 410 ClazzInstanceFactory property) 411 { 412 // Ignore - factories are not inherited 413 } 414 415 public void instanceFactoryRemoved( 416 Clazz clazz, 417 ClazzInstanceFactory property) 418 { 419 // Ignore - factories are not inherited 420 } 421 }; 422 }