View Javadoc

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 }