1 package net.sf.jldapbeans.lang; 2 3 import static java.util.Collections.addAll; 4 5 import java.util.Collection; 6 import java.util.Vector; 7 8 import net.sf.cglib.proxy.Enhancer; 9 import net.sf.jldapbeans.lang.annotation.LdapBean; 10 import net.sf.jldapbeans.lang.annotation.LdapModifier; 11 12 import org.apache.commons.beanutils.BeanUtils; 13 import org.apache.commons.lang.builder.EqualsBuilder; 14 import org.apache.commons.lang.builder.HashCodeBuilder; 15 import org.apache.commons.lang.builder.ToStringBuilder; 16 import org.apache.commons.logging.Log; 17 import org.apache.commons.logging.LogFactory; 18 19 /*** 20 * Representation of a jLDAPBeans' class. 21 * <p> 22 * The <code>LdapClass</code> is used as a LDAP bean instantiator. We can use it in many forms 23 * such as: 24 * <ul> 25 * <li>To instantiate a bean from a string array of <code>objectClass</code>es. 26 * <li>To identify which of the classes from an object are LDAP bean definitions. 27 * <li>Aggregate attributes merging <code>Class</code>es or <code>LdapClass</code>es objects. 28 * </ul> 29 * 30 * @author <a href="mailto:alonsoft@users.sf.net">A. Alonso Domínguez</a> 31 * @version 1.0 $ Revision 13-nov-2005 :: alonso $ 32 */ 33 public final class LdapClass { 34 public static final BeanRepository BEAN_REPOSITORY = BeanRepositoryFactory.getRepository(); 35 36 private static final Log log = LogFactory.getLog(LdapClass.class); 37 38 /*** 39 * Aggregates an array of any kind of java classes to an LDAP bean instance. 40 * <p> 41 * There is one thing that must be in mind of people who use this method to aggregate 42 * <tt>objectClass</tt>es to an already existent instance and that things is that the 43 * properties of the classes aggregated are not initialized by this method. 44 * 45 * @param instance An already existent instance of a LDAP bean. 46 * @param classes Any kind of java classes to be aggregated to the instance 47 * @return A new instance which is a copy of the one received but which has new properties 48 * received from the java classes aggregated. 49 * @throws LdapClassException If it is not possible aggregate classes. 50 */ 51 public static Object aggregate(Object obj, Class<?>... classes) throws LdapClassException { 52 Vector<Class<?>> classVector = new Vector<Class<?>>(); 53 addAll(classVector, classOf(obj).getClasses()); 54 addAll(classVector, filterBeanClasses(classes)); 55 56 LdapClass ldapClass = merge(classVector.toArray(new Class<?>[classVector.size()])); 57 Object result = ldapClass.newInstance(); 58 try { 59 BeanUtils.copyProperties(obj, result); 60 } 61 catch(Exception e) { 62 throw new LdapClassException(e.getMessage(), e); 63 } 64 return result; 65 } 66 67 /*** 68 * Aggregates an array of any kind of LDAP classes to an LDAP bean instance. 69 * <p> 70 * There is one thing that must be in mind of people who use this method to aggregate 71 * <tt>objectClass</tt>es to an already existent instance and that things is that the 72 * properties of the classes aggregated are not initialized by this method. 73 * 74 * @param instance An already existent instance of a LDAP bean. 75 * @param classes Any kind of LDAP classes to be aggregated to the instance 76 * @return A new instance which is a copy of the one received but which has new properties 77 * received from the java classes aggregated. 78 * @throws LdapClassException If it is not possible aggregate classes. 79 */ 80 public static Object aggregate(Object obj, LdapClass... ldapClasses) throws LdapClassException { 81 Vector<LdapClass> classVector = new Vector<LdapClass>(); 82 classVector.addElement(classOf(obj)); 83 addAll(classVector, ldapClasses); 84 85 LdapClass ldapClass = merge(classVector.toArray(new LdapClass[classVector.size()])); 86 Object result = ldapClass.newInstance(); 87 try { 88 BeanUtils.copyProperties(obj, result); 89 } 90 catch(Exception e) { 91 throw new LdapClassException(e.getMessage(), e); 92 } 93 return result; 94 } 95 96 /*** 97 * Obtains a <tt>LdapClass</tt> instance for an object that may be a LDAP bean. 98 * 99 * @param obj The object where search for LDAP classes. 100 * @return A new <code>LdapClass</code> instance that represents the LDAP classes of the 101 * object specified. 102 * @throws LdapClassException If the object doesn't implement any LDAP class 103 */ 104 public static LdapClass classOf(Object obj) throws LdapClassException { 105 return merge(getClassesOfBean(obj)); 106 } 107 108 private static Class<?>[] filterBeanClasses(Class<?>... classes) { 109 Vector<Class<?>> classVector = new Vector<Class<?>>(); 110 111 for(Class<?> clazz : classes) { 112 if(!clazz.isAnnotationPresent(LdapBean.class)) continue; 113 if(!clazz.isInterface()) { 114 log.warn("Class [" + clazz.getName() + "] ignored because it is not an interface."); 115 continue; 116 } 117 if(!classVector.contains(clazz)) 118 classVector.addElement(clazz); 119 } 120 121 return classVector.toArray(new Class<?>[classVector.size()]); 122 } 123 124 /*** 125 * Searches in <tt>BeanRepository</tt> for the names specified as <tt>objectClass</tt> 126 * maps each one as a <tt>java.lang.Class</tt>. 127 * 128 * @param objectClasses Array of <tt>objectClass</tt>es we need to search for. 129 * @return A new <tt>LdapClass</tt> instance that represents all the <tt>objectClass</tt>es 130 * specified. 131 * @throws LdapClassException If we can not found anyone of the <tt>objectClass</tt>es. 132 * @throws ClassNotFoundException If we can not fond anyone of the java classes. 133 */ 134 public static LdapClass forName(String... objectClasses) 135 throws LdapClassException, ClassNotFoundException { 136 Vector<Class<?>> classVector = new Vector<Class<?>>(); 137 for(String objectClass : objectClasses) { 138 String className = BEAN_REPOSITORY.getObjectClass(objectClass); 139 if(className == null) throw new ObjectClassNotFoundException(objectClass); 140 141 log.trace("Mapped objectClass [" + objectClass + "] as [" + className + "]"); 142 Class<?> clazz = Class.forName(className); 143 classVector.addElement(clazz); 144 } 145 146 return merge(classVector.toArray(new Class<?>[classVector.size()])); 147 } 148 149 private static Class<?>[] getClassesOfBean(Object obj) { 150 Vector<Class<?>> classVector = new Vector<Class<?>>(); 151 Class<?>[] classes = filterBeanClasses((Class<?>[]) obj.getClass().getInterfaces()); 152 153 addAll(classVector, classes); 154 for(Class<?> clazz : classes) { 155 log.trace("Found LdapJB class name [" + clazz.getName() + "]"); 156 scanParentClasses(clazz.getSuperclass(), classVector); 157 } 158 return classVector.toArray(new Class<?>[classVector.size()]); 159 } 160 161 /*** 162 * Identifies if a given object is an instance of an LdapJB 163 * 164 * @param obj A possible LdapJB instance 165 * @return <tt>true</tt> if it's a bean; <tt>false</tt> otherwise. 166 */ 167 public static boolean isLdapBean(Object obj) { 168 Class<?>[] classes = getClassesOfBean(obj); 169 if(classes.length == 0) return false; 170 return true; 171 } 172 173 /*** 174 * Merges an array of any kind of java class into a <tt>LdapClass</tt> instance indentifying 175 * which of the classes received are valid LDAP bean classes. 176 * 177 * @param classes An array of any kind of java class. 178 * @return A new instance of a <tt>LdapClass</tt> which will be conformant with the parameters 179 * received. 180 * @throws LdapClassException If there is not any valid LDAP bean class. 181 */ 182 public static LdapClass merge(Class<?>... classes) throws LdapClassException { 183 Class<?>[] clazzes = filterBeanClasses(classes); 184 if(clazzes.length == 0) 185 throw new LdapClassException("Didn't found any valid LJB class."); 186 return new LdapClass(clazzes); 187 } 188 189 /*** 190 * Merges an array of instances of <tt>LdapClass</tt> 191 * 192 * @param beanClasses A <tt>LdapClass</tt> array 193 * @return A new <tt>LdapClass</tt> that represents the <tt>LdapClass</tt>es received 194 * @throws LdapClassException 195 */ 196 public static LdapClass merge(LdapClass... ldapClasses) throws LdapClassException { 197 Vector<Class<?>> classVector = new Vector<Class<?>>(); 198 for(LdapClass ldapClass : ldapClasses) 199 addAll(classVector, ldapClass.getClasses()); 200 return merge(classVector.toArray(new Class<?>[classVector.size()])); 201 } 202 203 /*** 204 * Merges a <tt>LdapClass</tt> instance with an array of any kind of java classes. 205 * 206 * @param ldapClass Any <tt>LdapClass</tt> instance 207 * @param classes An array of any kind of java classes 208 * @return A new instance of a <tt>LdapClass</tt> which will be an aggegation of 209 * <tt>ldapClass</tt> and the valid java classes found in the array 210 * @throws LdapClassException If the is not any valid LDAP bean class in the array. 211 */ 212 public static LdapClass merge(LdapClass ldapClass, Class<?>... classes) throws LdapClassException { 213 Vector<Class<?>> classVector = new Vector<Class<?>>(); 214 addAll(classVector, ldapClass.getClasses()); 215 addAll(classVector, filterBeanClasses(classes)); 216 return merge(classVector.toArray(new Class<?>[classVector.size()])); 217 } 218 219 private static void scanParentClasses(Class<?> clazz, Collection<Class<?>> classes) { 220 if(clazz == null) return; 221 if(!classes.contains(clazz)) { 222 if(!clazz.isAnnotationPresent(LdapBean.class)) return; 223 224 log.trace("Found LdapJB class name [" + clazz.getName() + "]"); 225 classes.add(clazz); 226 scanParentClasses(clazz.getSuperclass(), classes); 227 } 228 } 229 230 private Class<?>[] m_classes; 231 232 private LdapClass(Class<?>... classes) { 233 if(classes == null) 234 throw new NullPointerException("classes"); 235 if(classes.length == 0) 236 throw new IllegalArgumentException("Argument classes can not be an empty array."); 237 238 m_classes = classes; 239 } 240 241 public boolean equals(Object o) { 242 if(this == o) return true; 243 if(!(o instanceof LdapClass)) return false; 244 245 final LdapClass other = (LdapClass) o; 246 return new EqualsBuilder() 247 .append(m_classes, other.m_classes) 248 .isEquals(); 249 } 250 251 /*** 252 * Obtains an array of java classes that reflect the ABSTRACT objectClasses 253 * used in this <tt>LdapClass</tt> 254 * 255 * @return An array of LdapJB interfaces 256 */ 257 public Class<?>[] getAbstractClasses() { 258 Vector<Class<?>> classVector = new Vector<Class<?>>(); 259 for(Class<?> clazz : m_classes) { 260 LdapBean beanMeta = clazz.getAnnotation(LdapBean.class); 261 if(beanMeta.value().equals(LdapModifier.ABSTRACT)) 262 classVector.addElement(clazz); 263 } 264 return classVector.toArray(new Class<?>[classVector.size()]); 265 } 266 267 /*** 268 * Obtains an array of java classes that reflect the AUXILIARY objectClasses 269 * used in this <tt>LdapClass</tt>. 270 * 271 * @return An array of LdapJB interfaces 272 */ 273 public Class<?>[] getAuxiliaryClasses() { 274 Vector<Class<?>> classVector = new Vector<Class<?>>(); 275 for(Class<?> clazz : m_classes) { 276 LdapBean beanMeta = clazz.getAnnotation(LdapBean.class); 277 if(beanMeta.value().equals(LdapModifier.AUXILIARY)) 278 classVector.addElement(clazz); 279 } 280 return classVector.toArray(new Class<?>[classVector.size()]); 281 } 282 283 /*** 284 * Accesses the class array used used inside this <tt>LdapClass</tt> instance. 285 * 286 * @return An array of LdapJB interfaces 287 */ 288 protected Class<?>[] getClasses() { 289 return m_classes; 290 } 291 292 /*** 293 * Obtains an array of java classes that reflect the STRUCTURAL objectClasses 294 * used in this <tt>LdapClass</tt>. 295 * 296 * @return An array of LdapJB interfaces 297 */ 298 public Class<?>[] getStructuralClasses() { 299 Vector<Class<?>> classVector = new Vector<Class<?>>(); 300 for(Class<?> clazz : m_classes) { 301 LdapBean beanMeta = clazz.getAnnotation(LdapBean.class); 302 if(beanMeta.value().equals(LdapModifier.STRUCTURAL)) 303 classVector.addElement(clazz); 304 } 305 return classVector.toArray(new Class<?>[classVector.size()]); 306 } 307 308 public int hashCode() { 309 return new HashCodeBuilder() 310 .append(m_classes) 311 .toHashCode(); 312 } 313 314 /*** 315 * Creates a new instance of a LDAP bean using a <tt>net.sf.cglib.proxy.Enhancer</tt> to 316 * support the implementation of every java class that the bean must support. 317 * 318 * @return An instance of a LDAP bean. 319 * @throws BeanInstantiationException If some exception is catched during the instantiation 320 * process. 321 */ 322 public Object newInstance() throws BeanInstantiationException { 323 return LdapClassHelper.newInstance(this); 324 } 325 326 public String toString() { 327 return new ToStringBuilder(this) 328 .append(m_classes) 329 .toString(); 330 } 331 332 }