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

COVERAGE SUMMARY FOR SOURCE FILE [QueryObjectGenerator.java]

nameclass, %method, %block, %line, %
QueryObjectGenerator.java100% (2/2)97%  (30/31)89%  (971/1089)91%  (204.7/226)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class QueryObjectGenerator100% (1/1)97%  (29/30)89%  (956/1074)90%  (198.7/220)
create (Class, List): Class 0%   (0/1)0%   (0/6)0%   (0/1)
getFieldValue (String): int 100% (1/1)27%  (14/52)25%  (3/12)
isFieldInDefinitionClass (String): boolean 100% (1/1)41%  (14/34)62%  (5/8)
addFieldIfNeeded (ClassEmitter, String, Type): void 100% (1/1)66%  (58/88)74%  (10.3/14)
addQueryMethod (ClassEmitter, Mapper): void 100% (1/1)83%  (59/71)93%  (13/14)
printDebugInfo (Class, List): void 100% (1/1)92%  (67/73)85%  (9.4/11)
create (Class, List, Class): Class 100% (1/1)93%  (84/90)91%  (20/22)
<static initializer> 100% (1/1)100% (4/4)100% (2/2)
QueryObjectGenerator (Customizer, SQLDialect): void 100% (1/1)100% (14/14)100% (5/5)
addBaseQueryMethods (ClassEmitter): void 100% (1/1)100% (33/33)100% (5/5)
addConstrcutor (ClassEmitter, Constructor): void 100% (1/1)100% (40/40)100% (11/11)
addConstructorAndFields (ClassEmitter): void 100% (1/1)100% (123/123)100% (24/24)
addGetter (ClassEmitter, String, String): void 100% (1/1)100% (46/46)100% (7/7)
addGetterAndSetter (ClassEmitter, String, String): void 100% (1/1)100% (11/11)100% (3/3)
addPagingMethods (ClassEmitter): void 100% (1/1)100% (21/21)100% (5/5)
addSetter (ClassEmitter, String, String): void 100% (1/1)100% (51/51)100% (8/8)
addSetterForPaging (ClassEmitter, String, String): void 100% (1/1)100% (53/53)100% (9/9)
addStaticInitializer (ClassEmitter, List): void 100% (1/1)100% (50/50)100% (15/15)
addToString (ClassEmitter): void 100% (1/1)100% (24/24)100% (5/5)
beginClass (ClassEmitter): void 100% (1/1)100% (48/48)100% (8/8)
createClassNameType (String): String 100% (1/1)100% (14/14)100% (1/1)
emitGetField (CodeEmitter, String): void 100% (1/1)100% (15/15)100% (3/3)
emitPutField (CodeEmitter, String): void 100% (1/1)100% (15/15)100% (3/3)
endClass (ClassEmitter): void 100% (1/1)100% (3/3)100% (2/2)
getAdapterFieldName (Class): String 100% (1/1)100% (6/6)100% (1/1)
getClassNameType (): String 100% (1/1)100% (3/3)100% (1/1)
getCustomizer (): Customizer 100% (1/1)100% (3/3)100% (1/1)
getDynamicAdapterClasses (List): Set 100% (1/1)100% (77/77)100% (17/17)
getImplementPaging (): boolean 100% (1/1)100% (3/3)100% (1/1)
getSqlDialect (): SQLDialect 100% (1/1)100% (3/3)100% (1/1)
     
class QueryObjectGenerator$FieldInfo100% (1/1)100% (1/1)100% (15/15)100% (6/6)
QueryObjectGenerator$FieldInfo (int, String, Type, Type): void 100% (1/1)100% (15/15)100% (6/6)

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.codegen;
20 
21import static sf.qof.codegen.Constants.FIELD_NAME_BATCH_SIZE;
22import static sf.qof.codegen.Constants.FIELD_NAME_DEFAULT_BATCH_SIZE;
23import static sf.qof.codegen.Constants.FIELD_NAME_DEFAULT_FETCH_SIZE;
24import static sf.qof.codegen.Constants.FIELD_NAME_FETCH_SIZE;
25import static sf.qof.codegen.Constants.FIELD_NAME_FIRST_RESULT;
26import static sf.qof.codegen.Constants.FIELD_NAME_MAX_RESULTS;
27import static sf.qof.codegen.Constants.SIG_init;
28import static sf.qof.codegen.Constants.SIG_toString;
29import static sf.qof.codegen.Constants.TYPE_SQLException;
30import static sf.qof.codegen.Constants.TYPE_int;
31 
32import java.io.BufferedOutputStream;
33import java.io.File;
34import java.io.FileOutputStream;
35import java.io.IOException;
36import java.io.OutputStream;
37import java.lang.reflect.Constructor;
38import java.lang.reflect.Field;
39import java.lang.reflect.Modifier;
40import java.util.ArrayList;
41import java.util.HashMap;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Map;
45import java.util.Set;
46 
47import net.sf.cglib.core.ClassEmitter;
48import net.sf.cglib.core.CodeEmitter;
49import net.sf.cglib.core.Constants;
50import net.sf.cglib.core.DebuggingClassWriter;
51import net.sf.cglib.core.Signature;
52 
53import org.objectweb.asm.ClassWriter;
54import org.objectweb.asm.Type;
55 
56import sf.qof.BaseQuery;
57import sf.qof.Paging;
58import sf.qof.adapter.DynamicMappingAdapter;
59import sf.qof.customizer.Customizer;
60import sf.qof.dialect.SQLDialect;
61import sf.qof.exception.ValidationException;
62import sf.qof.mapping.AdapterMapping;
63import sf.qof.mapping.Mapper;
64import sf.qof.mapping.Mapping;
65import sf.qof.mapping.MappingAdapter;
66import sf.qof.mapping.QueryType;
67import sf.qof.util.DefineClassHelper;
68import sf.qof.util.ReflectionUtils;
69 
70/**
71 * Internal - QueryObjectGenerator is the main generator class for query objects.
72 */
73public class QueryObjectGenerator {
74 
75  /**
76   * Default fetch size used if not defined in query definition class.
77   */
78  public static final int DEFAULT_FETCH_SIZE = 100;
79  
80  /**
81   * Default batch size used if not defined in query definition class.
82   */
83  public static final int DEFAULT_BATCH_SIZE = 100;
84 
85  private Customizer customizer;
86  private Class<?> queryDefinitionClass;
87  private Class<?> superClass;
88  private String classNameType;
89  private Map<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
90  private SQLDialect sqlDialect;
91  private boolean implementPaging;
92 
93  public QueryObjectGenerator(Customizer customizer, SQLDialect sqlDialect) {
94          this.customizer = customizer;
95          this.sqlDialect = sqlDialect;
96  }
97  
98  public SQLDialect getSqlDialect() {
99    return sqlDialect;
100  }
101  
102  public boolean getImplementPaging() {
103    return implementPaging;
104  }
105  
106  public String getClassNameType() {
107    return classNameType;
108  }
109  
110  public Customizer getCustomizer() {
111    return customizer;
112  }
113  
114  public <T> Class<T> create(Class<T> queryDefinitionClass, List<Mapper> mappers) {
115    return create(queryDefinitionClass, mappers, Object.class);
116  }
117 
118  public <T> Class<T> create(Class<T> queryDefinitionClass, List<Mapper> mappers, Class<?> superClass) {
119    this.queryDefinitionClass = queryDefinitionClass;
120    this.superClass = superClass;
121    implementPaging = Paging.class.isAssignableFrom(queryDefinitionClass);
122    try {
123      String className = customizer.getClassName(queryDefinitionClass);
124      classNameType = createClassNameType(className);
125 
126      if (debugLocation != null) {
127        printDebugInfo(queryDefinitionClass, mappers);
128      }
129 
130      ClassWriter cw = new DebuggingClassWriter(true);
131      ClassEmitter ce = new ClassEmitter(cw);
132 
133      beginClass(ce);
134      addStaticInitializer(ce, mappers);
135      addConstructorAndFields(ce);
136      addBaseQueryMethods(ce);
137      if (implementPaging) {
138        addPagingMethods(ce);
139      }
140      addToString(ce);
141      for (Mapper mapper : mappers) {
142        addQueryMethod(ce, mapper);
143      }
144      endClass(ce);
145 
146      return DefineClassHelper.defineClass(className, cw.toByteArray(),
147                  queryDefinitionClass.getClassLoader());
148 
149    } catch (Exception e) {
150      throw new RuntimeException(e);
151    }
152  }
153 
154  private String createClassNameType(String className) {
155        return "L" + className.replace('.', '/') + ";";
156  }
157 
158  private static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
159 
160  private static String debugLocation;
161 
162  static {
163    debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
164  }
165 
166  private void printDebugInfo(Class<?> queryDefinitionClass, List<Mapper> mappers) {
167    String dirs = customizer.getClassName(queryDefinitionClass).replace('.', File.separatorChar);
168    try {
169      new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
170 
171      File file = new File(new File(debugLocation), dirs + ".map");
172      OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
173 
174      try {
175        for (Mapper mapper : mappers) {
176          mapper.printMappingInfo(out);
177        }
178      } finally {
179        out.close();
180      }
181    } catch (IOException e) {
182    }
183  }
184 
185  private void beginClass(ClassEmitter ce) {
186    List<Type> interfaceTypes = new ArrayList<Type>();
187    interfaceTypes.add(Type.getType(BaseQuery.class));
188    if (queryDefinitionClass.isInterface()) {
189      interfaceTypes.add(Type.getType(queryDefinitionClass));
190    }
191    if (implementPaging) {
192      interfaceTypes.add(Type.getType(Paging.class));
193    }
194    ce.begin_class(Constants.V1_2, Constants.ACC_PUBLIC, customizer.getClassName(queryDefinitionClass), Type
195        .getType(superClass), (Type[]) interfaceTypes.toArray(new Type[interfaceTypes.size()]), "<generated>");
196  }
197 
198  private void endClass(ClassEmitter ce) {
199    ce.end_class();
200  }
201 
202  private void addQueryMethod(ClassEmitter ce, Mapper mapper) {
203    CodeEmitter co;
204    // int numberOfPersons() throws SQLException
205 
206    int access = Constants.ACC_PUBLIC;
207    if (Modifier.isProtected(mapper.getMethod().getModifiers())) {
208      access = Constants.ACC_PROTECTED;
209    }
210    co = ce.begin_method(access, mapper.getMethod().getSignature(), new Type[] { TYPE_SQLException }, null);
211 
212    QueryType queryType = mapper.getQueryType();
213    if (queryType == QueryType.QUERY) {
214      SelectQueryMethodGenerator.addSelectQueryBody(co, this, mapper);
215    } else if (queryType == QueryType.INSERT || queryType == QueryType.UPDATE || queryType == QueryType.DELETE) {
216      InsertUpdateDeleteQueryMethodGenerator.addInsertUpdateDeleteQueryBody(co, this, mapper);
217    } else if (queryType == QueryType.CALL) {
218      CallQueryMethodGenerator.addCallQueryBody(co, this, mapper);
219    } else {
220      throw new RuntimeException("Not supported query type: " + queryType);
221    }
222 
223    co.end_method();
224  }
225 
226  private void addToString(ClassEmitter ce) {
227    CodeEmitter co;
228    co = ce.begin_method(Constants.ACC_PUBLIC, SIG_toString, null, null);
229    co.push("QueryObject generated from " + queryDefinitionClass.getName());
230    co.return_value();
231    co.end_method();
232  }
233 
234  private void addBaseQueryMethods(ClassEmitter ce) {
235          customizer.getConnectionFactoryCustomizer(queryDefinitionClass).emitGetConnection(queryDefinitionClass, superClass, ce);
236          customizer.getConnectionFactoryCustomizer(queryDefinitionClass).emitSetConnection(queryDefinitionClass, superClass, ce);
237    addGetterAndSetter(ce, FIELD_NAME_FETCH_SIZE, "I");
238    addGetterAndSetter(ce, FIELD_NAME_BATCH_SIZE, "I");
239  }
240 
241  private void addPagingMethods(ClassEmitter ce) {
242    addFieldIfNeeded(ce, FIELD_NAME_FIRST_RESULT, TYPE_int);
243    addFieldIfNeeded(ce, FIELD_NAME_MAX_RESULTS, TYPE_int);
244    
245    addSetterForPaging(ce, FIELD_NAME_FIRST_RESULT, "I");
246    addSetterForPaging(ce, FIELD_NAME_MAX_RESULTS, "I");
247  }
248  
249  private void addGetterAndSetter(ClassEmitter ce, String fieldName, String fieldType) {
250    addGetter(ce, fieldName, fieldType);
251    addSetter(ce, fieldName, fieldType);
252  }
253  
254  private void addSetterForPaging(ClassEmitter ce, String fieldName, String fieldType) {
255    CodeEmitter co;
256    Signature signature;
257    // void setField(type)
258    signature = new Signature("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), "("
259        + fieldType + ")Lsf/qof/Paging;");
260    co = ce.begin_method(Constants.ACC_PUBLIC, signature, null, null);
261    co.load_this();
262    co.load_arg(0);
263    emitPutField(co, fieldName);
264    co.load_this();
265    co.return_value();
266    co.end_method();
267  }
268  
269  private void addSetter(ClassEmitter ce, String fieldName, String fieldType) {
270    CodeEmitter co;
271    Signature signature;
272    // void setField(type)
273    signature = new Signature("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), "("
274        + fieldType + ")V");
275    co = ce.begin_method(Constants.ACC_PUBLIC, signature, null, null);
276    co.load_this();
277    co.load_arg(0);
278    emitPutField(co, fieldName);
279    co.return_value();
280    co.end_method();
281  }
282  
283  private void addGetter(ClassEmitter ce, String fieldName, String fieldType) {
284    CodeEmitter co;
285    Signature signature;
286    // type getField()
287    signature = new Signature("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), "()"
288        + fieldType);
289    co = ce.begin_method(Constants.ACC_PUBLIC, signature, null, null);
290    co.load_this();
291    emitGetField(co, fieldName);
292    co.return_value();
293    co.end_method();
294  }
295 
296  private void addStaticInitializer(ClassEmitter ce, List<Mapper> mappers) {
297    // create a private static final field for each dynamic adapter
298    Set<Class<?>> dynamicAdapters = getDynamicAdapterClasses(mappers);
299    if (dynamicAdapters.size() > 0) {
300      // static initializer
301      CodeEmitter co = ce.begin_static();
302      for (Class<?> dynamicAdapterClass : dynamicAdapters) {
303        String fieldName = getAdapterFieldName(dynamicAdapterClass);
304        Type fieldType = Type.getType(dynamicAdapterClass);
305        ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, fieldType, null, null);
306        co.new_instance(fieldType);
307        co.dup();
308        co.invoke_constructor(fieldType);
309        co.putfield(fieldName);
310      }
311      co.return_value();
312      co.end_method();
313    }
314  }
315 
316  private void addConstructorAndFields(ClassEmitter ce) {
317    // fields
318    if (!isFieldInDefinitionClass(FIELD_NAME_DEFAULT_BATCH_SIZE)) {
319      ce.declare_field(Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_STATIC, 
320        FIELD_NAME_DEFAULT_BATCH_SIZE, TYPE_int, new Integer(DEFAULT_BATCH_SIZE), null);
321    }
322    if (!isFieldInDefinitionClass(FIELD_NAME_DEFAULT_FETCH_SIZE)) {
323      ce.declare_field(Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_STATIC, 
324        FIELD_NAME_DEFAULT_FETCH_SIZE, TYPE_int, new Integer(DEFAULT_FETCH_SIZE), null);
325    }
326 
327          customizer.getConnectionFactoryCustomizer(queryDefinitionClass).emitFields(queryDefinitionClass, superClass, ce);
328 
329    addFieldIfNeeded(ce, FIELD_NAME_BATCH_SIZE, TYPE_int);
330    addFieldIfNeeded(ce, FIELD_NAME_FETCH_SIZE, TYPE_int);
331    
332    CodeEmitter co;
333    // init method
334    co = ce.begin_method(Constants.ACC_PUBLIC, SIG_init, null, null);
335    co.load_this();
336    if (isFieldInDefinitionClass(FIELD_NAME_DEFAULT_BATCH_SIZE)) {
337      co.push(getFieldValue(FIELD_NAME_DEFAULT_BATCH_SIZE));
338    } else {
339      co.push(DEFAULT_BATCH_SIZE);
340    }
341    emitPutField(co, FIELD_NAME_BATCH_SIZE);
342    co.load_this();
343    if (isFieldInDefinitionClass(FIELD_NAME_DEFAULT_FETCH_SIZE)) {
344      co.push(getFieldValue(FIELD_NAME_DEFAULT_FETCH_SIZE));
345    } else {
346      co.push(DEFAULT_FETCH_SIZE);
347    }
348    emitPutField(co, FIELD_NAME_FETCH_SIZE);
349    co.return_value();
350    co.end_method();
351 
352    Constructor<?>[] superConstructors = superClass.getDeclaredConstructors();
353    for (Constructor<?> superConstuctor : superConstructors) {
354      addConstrcutor(ce, superConstuctor);
355    }
356  }
357 
358  private void addConstrcutor(ClassEmitter ce, Constructor<?> superConstructor) {
359    Signature sigConstructor = ReflectionUtils.getConstructorSignature(superConstructor);
360    CodeEmitter co = ce.begin_method(superConstructor.getModifiers(), sigConstructor, null, null);
361    co.load_this();
362    for (int i = 0; i < superConstructor.getParameterTypes().length; i++) {
363      co.load_arg(i);
364    }
365    co.invoke_constructor(ce.getSuperType(), sigConstructor);
366    // call init
367    co.load_this();
368    co.invoke_virtual_this(SIG_init);
369    co.return_value();
370    co.end_method();
371  }
372 
373  private void addFieldIfNeeded(ClassEmitter ce, String fieldName, Type fieldType) {
374    Field field = null;
375    try {
376      field = superClass.getDeclaredField(fieldName);
377      if (!Type.getType(field.getType()).equals(fieldType)) {
378        throw new ValidationException("Class has field '" + fieldName + "' that is wrong type");
379      }
380      if (Modifier.isPrivate(field.getModifiers())) {
381        // warning?
382        throw new ValidationException("Class has private field '" + fieldName + "'");
383      }
384    } catch (SecurityException e) {
385     // ignore
386    } catch (NoSuchFieldException e) {
387     // ignore
388    }
389    if (field == null) {
390      ce.declare_field(Constants.ACC_PRIVATE, fieldName, fieldType, null, null);
391      fields.put(fieldName, new FieldInfo(Constants.ACC_PRIVATE, fieldName, ce.getClassType(), fieldType));
392    } else {
393      fields.put(fieldName, new FieldInfo(field.getModifiers(), fieldName, Type.getType(superClass), fieldType));
394    }
395  }
396 
397  private Set<Class<?>> getDynamicAdapterClasses(List<Mapper> mappers) {
398    Set<Class<?>> dynamicAdapters = new HashSet<Class<?>>();
399    for (Mapper mapper : mappers) {
400      if (mapper.getParameters() != null) {
401        for (Mapping mapping : mapper.getParameters()) {
402          if (mapping instanceof AdapterMapping) {
403            MappingAdapter adapter = ((AdapterMapping) mapping).getAdapter();
404            if (adapter instanceof DynamicMappingAdapter) {
405              dynamicAdapters.add(adapter.getClass());
406            }
407          }
408        }
409      }
410      if (mapper.getResults() != null) {
411        for (Mapping mapping : mapper.getResults()) {
412          if (mapping instanceof AdapterMapping) {
413            MappingAdapter adapter = ((AdapterMapping) mapping).getAdapter();
414            if (adapter instanceof DynamicMappingAdapter) {
415              dynamicAdapters.add(adapter.getClass());
416            }
417          }
418        }
419      }
420    }
421    return dynamicAdapters;
422  }
423 
424  public static String getAdapterFieldName(Class<?> adapterClass) {
425    return adapterClass.getName().replace('.', '$');
426  }
427 
428  private boolean isFieldInDefinitionClass(String name) {
429    Field field;
430    try {
431      field = queryDefinitionClass.getField(name);
432      if (field.getType() != int.class) {
433        throw new ValidationException("Field " + name + " must be of type int");
434      }
435      return true;
436    } catch (SecurityException e) {
437      throw new RuntimeException(e);
438    } catch (NoSuchFieldException e) {
439      return false;
440    }
441  }
442 
443  private int getFieldValue(String name) {
444    Field field;
445    try {
446      field = queryDefinitionClass.getField(name);
447      if (field.getType() != int.class) {
448        throw new ValidationException("Field " + name + " must be of type int");
449      }
450      return field.getInt(queryDefinitionClass);
451    } catch (SecurityException e) {
452      throw new RuntimeException(e);
453    } catch (NoSuchFieldException e) {
454      throw new RuntimeException(e);
455    } catch (IllegalArgumentException e) {
456      throw new RuntimeException(e);
457    } catch (IllegalAccessException e) {
458      throw new RuntimeException(e);
459    }
460  }
461 
462  private static class FieldInfo {
463    int access;
464    String name;
465    Type type;
466    Type owner;
467 
468    public FieldInfo(int access, String name, Type owner, Type type) {
469      this.access = access;
470      this.name = name;
471      this.owner = owner;
472      this.type = type;
473    }
474  }
475 
476  public void emitGetField(CodeEmitter co, String fieldName) {
477    FieldInfo info = fields.get(fieldName);
478    co.getfield(info.owner, info.name, info.type);
479  }
480 
481  public void emitPutField(CodeEmitter co, String fieldName) {
482    FieldInfo info = fields.get(fieldName);
483    co.putfield(info.owner, info.name, info.type);
484  }
485}

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