View Javadoc

1   /*
2    * Copyright 2004-2005 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.composite.specialize.specializers;
17  
18  import java.lang.reflect.InvocationHandler;
19  import java.lang.reflect.Proxy;
20  import java.util.List;
21  
22  import net.sf.composite.specialize.SpecializationException;
23  import net.sf.composite.specialize.Specializer;
24  import net.sf.composite.util.ClassUtils;
25  import net.sf.composite.util.ContainerUtils;
26  import net.sf.composite.util.DelegatingInvocationHandler;
27  import net.sf.composite.util.ObjectUtils;
28  
29  /***
30   * <p>
31   * A composite specializer that is implemented using JDK 1.3 proxies. This
32   * specializer requires that the composite to be specialized has a public
33   * no-argument constructor. It also requires that the specialized type be an
34   * interface, and any methods that are to be executed on the composite are
35   * expressed in a separate interface (i.e. they cannot be specified in the
36   * implementation class only). Finally, component access methods must be
37   * specified in an interface. The {@link net.sf.composite.SimpleComposite},
38   * {@link net.sf.composite.RegistryComposite} etc. interfaces can be used for
39   * this purpose.
40   * </p>
41   * 
42   * <p>
43   * The main benefit of this specializer is that it does not require the
44   * composite to implement all the interfaces to which it may be specialized. The
45   * main drawback is that the use of JDK proxies is much slower than regular
46   * method calls (such as you would get when using the
47   * {@link net.sf.composite.specialize.specializers.CloningSpecializer}.
48   * </p>
49   * 
50   * @author Matt Sgarlata
51   * @since Mar 11, 2005
52   */
53  public class Jdk13ProxySpecializer extends BaseSpecializer implements Specializer {
54  	
55  	protected boolean isSpecializableImpl(Object composite,
56  		Class specializedType) throws Exception {
57  		
58  		if (!specializedType.isInterface()) {
59  			throw new SpecializationException(ObjectUtils.getObjectDescription(specializedType) + " must be an interface");
60  		}
61  		
62  		List components = getComponentAccessor().getComponents(composite);
63  		
64  		return
65  			specializedType.isAssignableFrom(composite.getClass()) ||
66  			ContainerUtils.hasElementOfType(components, specializedType);
67  	}
68  	
69  	protected Object createNewComposite(Object composite) throws Exception {
70  		return composite.getClass().newInstance();
71  	}
72  	
73  	protected InvocationHandler createInvocationHandler(Object composite)
74  		throws Exception {
75  		return new DelegatingInvocationHandler(createNewComposite(composite));
76  	}
77  	
78  	protected Object specializeImpl(Object composite, Class specializedType) throws Exception {
79  		
80  		if (specializedType.isAssignableFrom(composite.getClass())) {
81  			return composite;
82  		}
83  
84  		Class[] interfaces = ClassUtils.getInterfaces(composite.getClass());
85  		Class[] newInterfaces = new Class[interfaces.length + 1];
86  		System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
87  		newInterfaces[interfaces.length] = specializedType;
88  
89  		// create the proxy
90  		Object newComposite = Proxy.newProxyInstance(
91  			specializedType.getClassLoader(), newInterfaces,
92  			createInvocationHandler(composite));
93  		// get the components in the old composite
94  		List components =
95  			getComponentAccessor().getComponents(composite);
96  		// filter out the components that implement the specializedType
97  		List specializedComponents =
98  			ContainerUtils.getElementsOfType(components, specializedType);
99  		// set the components of the new composite to those from the original
100 		// composite that implement the specializedType
101 		getComponentAccessor().setComponents(newComposite,
102 			specializedComponents);
103 		// return the new composite
104 		return newComposite;
105 		
106 	}
107 }