EMMA Coverage Report (generated Sat Nov 03 21:53:04 GMT 2007)
[all classes][sf.qof.util]

COVERAGE SUMMARY FOR SOURCE FILE [DelegatorFactory.java]

nameclass, %method, %block, %line, %
DelegatorFactory.java100% (1/1)90%  (9/10)90%  (455/503)90%  (87.5/97)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DelegatorFactory100% (1/1)90%  (9/10)90%  (455/503)90%  (87.5/97)
DelegatorFactory (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getTypes (Class []): Type [] 100% (1/1)57%  (12/21)63%  (2.5/4)
create (Class, Class, Object []): Object 100% (1/1)70%  (57/81)64%  (9/14)
createClass (Class, Class, Object []): Class 100% (1/1)85%  (66/78)87%  (13/15)
createConstructorAndFields (ClassEmitter, Class, Object []): void 100% (1/1)100% (88/88)100% (14/14)
createInitializeMethod (ClassEmitter, Class, Class, Object []): void 100% (1/1)100% (118/118)100% (21/21)
createMethod (ClassEmitter, Class, Method): void 100% (1/1)100% (64/64)100% (19/19)
createMethods (ClassEmitter, Class): void 100% (1/1)100% (22/22)100% (3/3)
getInterfaceTypes (Class): Type [] 100% (1/1)100% (6/6)100% (2/2)
getTypes (Object []): Type [] 100% (1/1)100% (22/22)100% (4/4)

1/*
2 * Copyright 2007 brunella ltd
3 *
4 * Licensed under the LGPL Version 3 (the "License");
5 * you may not use this file except in compliance with the License.
6 *
7 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
8 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
10 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
11 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
12 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
13 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
14 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
15 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
16 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
17 * THE POSSIBILITY OF SUCH DAMAGE.
18 */
19package sf.qof.util;
20 
21import static sf.qof.codegen.Constants.TYPE_boolean;
22 
23import java.lang.reflect.Method;
24import java.lang.reflect.Modifier;
25 
26import net.sf.cglib.core.ClassEmitter;
27import net.sf.cglib.core.CodeEmitter;
28import net.sf.cglib.core.Constants;
29import net.sf.cglib.core.DebuggingClassWriter;
30import net.sf.cglib.core.Signature;
31 
32import org.objectweb.asm.ClassWriter;
33import org.objectweb.asm.Label;
34import org.objectweb.asm.Type;
35 
36/**
37 * Factory class to create a delegator object for a given delegatee class.
38 * 
39 * <p>
40 * The factory creates wrappers for each public non-final method of the
41 * delegatee class. Once any of these methods is called the delegatee factory is
42 * used to initialize the delegatee object. The delegatee class must have a
43 * default constructor.
44 * </p>
45 * <p>
46 * The delegatee factory must implement a static <code>initialize</code>
47 * method with the first argument being the delegatee object and the other
48 * arguments matching the types of the <code>constructorParameters</code>:
49 * </p>
50 * 
51 * <pre>
52 * public class PersonDelegateeFactory {
53 *   public static void initialize(Person personDelegatee, Integer id) {
54 *     String name = getNameForId(id);
55 *     personDelegatee.setName(name);
56 *   }
57 * }
58 * 
59 * // create a delegator for person and pass it the id of 1 
60 * Person person = DelegatorFactory&lt;Person&gt;(Person.class, 
61 *      PersonDelegateeFactory.class, 1);
62 * // person is instanziated but not initialized
63 * // a call to getName() or any other public method of person for the first time
64 * // calls PersonDelegateeFactory.initialize(person, 1)
65 * String name = person.getName();
66 * // person is initialized now
67 * </pre>
68 * 
69 * <p>
70 * Initialization of delegator objects is thread-safe.
71 * </p>
72 * <p>
73 * <code>DelegatorFactory</code> can be used to implement lazy initialization
74 * of objects
75 * </p>
76 * <p>
77 * The generated delegatee objects super class is the delegatee class.
78 * </p>
79 */
80public class DelegatorFactory {
81 
82  /**
83   * Returns an instance of a delegator object for a delegatee class.
84   * 
85   * @param <T>
86   *          type of the delegatee object
87   * @param delegateeClass
88   *          class of the delegatee object
89   * @param delegateeFactory
90   *          factory to initialize the delegatee object
91   * @param constructorParameters
92   *          parameters that are passed to the delegatee factory
93   * @return delegator object instance
94   */
95  @SuppressWarnings("unchecked")
96  public static <T> T create(Class<T> delegateeClass, Class<?> delegateeFactory, Object... constructorParameters) {
97    Class<T> delegatorClass = (Class<T>) ClassGenerationCache.getCachedClass(delegateeClass, delegateeFactory);
98    if (delegatorClass == null) {
99      try {
100        // create new class
101        delegatorClass = createClass(delegateeClass, delegateeFactory, constructorParameters);
102 
103        // put the newly created class
104        ClassGenerationCache.putCachedClass(delegatorClass, delegateeClass, delegateeFactory);
105      } catch (Throwable e) {
106        ClassGenerationCache.putCachedClass(null, delegateeClass, delegateeFactory);
107        throw new RuntimeException(e);
108      }
109    }
110 
111    // create instance
112    Class[] constructorParameterTypes = new Class[constructorParameters.length];
113    for (int i = 0; i < constructorParameters.length; i++) {
114      constructorParameterTypes[i] = constructorParameters[i].getClass();
115    }
116    try {
117      return delegatorClass.getConstructor(constructorParameterTypes).newInstance(constructorParameters);
118    } catch (Throwable e) {
119      throw new RuntimeException(e);
120    }
121  }
122 
123  private static <T> Class<T> createClass(Class<T> delegateeClass, Class<?> delegateeFactory,
124      Object... constructorParameters) throws Exception {
125 
126    ClassWriter cw = new DebuggingClassWriter(true);
127    ClassEmitter ce = new ClassEmitter(cw);
128 
129    String className = delegateeClass.getName() + "$" + delegateeFactory.getName() + "$Delegator";
130    if (className.startsWith("java")) {
131      className = "$" + className;
132    }
133 
134    Type[] delegateeInterfaces = getInterfaceTypes(delegateeClass);
135 
136    ce.begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, Type.getType(delegateeClass), delegateeInterfaces,
137        "<generated>");
138 
139    createConstructorAndFields(ce, delegateeClass, constructorParameters);
140 
141    createInitializeMethod(ce, delegateeFactory, delegateeClass, constructorParameters);
142    createMethods(ce, delegateeClass);
143 
144    ce.end_class();
145 
146    ClassLoader classLoader = delegateeClass.getClassLoader();
147    if (classLoader == null) {
148      classLoader = DelegatorFactory.class.getClassLoader();
149    }
150 
151    return DefineClassHelper.defineClass(className, cw.toByteArray(), classLoader);
152  }
153 
154  private static void createConstructorAndFields(ClassEmitter ce, Class<?> delegateeClass,
155      Object[] constructorParameters) {
156    ce.declare_field(Constants.ACC_PRIVATE, "$$initialized", TYPE_boolean, false, null);
157    for (int i = 0; i < constructorParameters.length; i++) {
158      ce.declare_field(Constants.ACC_PRIVATE, "$$" + i, Type.getType(constructorParameters[i].getClass()), null, null);
159    }
160 
161    Signature sigConstructor = new Signature("<init>", Type.VOID_TYPE, getTypes(constructorParameters));
162    CodeEmitter co = ce.begin_method(Constants.ACC_PUBLIC, sigConstructor, null, null);
163    co.load_this();
164    co.invoke_constructor(ce.getSuperType(), new Signature("<init>", "()V"));
165    // store constructor parameters
166    for (int i = 0; i < constructorParameters.length; i++) {
167      co.load_this();
168      co.load_arg(i);
169      co.putfield("$$" + i);
170    }
171    co.return_value();
172    co.end_method();
173  }
174 
175  private static void createMethods(ClassEmitter ce, Class<?> delegateeClass) {
176    for (Method method : delegateeClass.getMethods()) {
177      createMethod(ce, delegateeClass, method);
178    }
179  }
180 
181  private static void createInitializeMethod(ClassEmitter ce, Class<?> delegateeFactory, Class<?> delegateeClass,
182      Object[] constructorParameters) {
183    CodeEmitter co = ce.begin_method(Constants.ACC_PRIVATE + Constants.ACC_SYNCHRONIZED, new Signature("$initialize",
184        "()V"), null, null);
185    co.load_this();
186    co.getfield("$$initialized");
187    Label labelInitialized = co.make_label();
188    co.if_jump(CodeEmitter.NE, labelInitialized);
189 
190    co.load_this();
191    co.push(true);
192    co.putfield("$$initialized");
193    co.load_this();
194    String desc = "(L" + Type.getType(delegateeClass).getInternalName() + ";";
195    for (int i = 0; i < constructorParameters.length; i++) {
196      desc += "L" + Type.getType(constructorParameters[i].getClass()).getInternalName() + ";";
197    }
198    desc += ")V";
199    for (int i = 0; i < constructorParameters.length; i++) {
200      co.load_this();
201      co.getfield("$$" + i);
202    }
203    co.invoke_static(Type.getType(delegateeFactory), new Signature("initialize", desc));
204 
205    co.mark(labelInitialized);
206 
207    co.return_value();
208    co.end_method();
209  }
210 
211  private static void createMethod(ClassEmitter ce, Class<?> delegateeClass, Method method) {
212    if (Modifier.isFinal(method.getModifiers())) {
213      return;
214    }
215    Signature methodSignature = ReflectionUtils.getMethodSignature(method);
216    CodeEmitter co = ce.begin_method(Constants.ACC_PUBLIC, methodSignature, getTypes(method.getExceptionTypes()), null);
217    // call delegatee
218    co.load_this();
219    co.getfield("$$initialized");
220    Label labelInitialized = co.make_label();
221    co.if_jump(CodeEmitter.NE, labelInitialized);
222    co.load_this();
223    co.invoke_virtual_this(new Signature("$initialize", "()V"));
224    co.mark(labelInitialized);
225    co.load_this();
226    int num = method.getParameterTypes().length;
227    for (int i = 0; i < num; i++) {
228      co.load_arg(i);
229    }
230    co.super_invoke();
231    co.return_value();
232    co.end_method();
233  }
234 
235  private static Type[] getInterfaceTypes(Class<?> clazz) {
236    Class<?>[] interfaces = clazz.getInterfaces();
237    return getTypes(interfaces);
238  }
239 
240  private static Type[] getTypes(Class<?>[] classes) {
241    Type[] types = new Type[classes.length];
242    for (int i = 0; i < classes.length; i++) {
243      types[i] = Type.getType(classes[i]);
244    }
245    return types;
246  }
247 
248  private static Type[] getTypes(Object[] objs) {
249    Type[] types = new Type[objs.length];
250    for (int i = 0; i < objs.length; i++) {
251      types[i] = Type.getType(objs[i].getClass());
252    }
253    return types;
254  }
255}

[all classes][sf.qof.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov