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

COVERAGE SUMMARY FOR SOURCE FILE [SelectQueryMethodGenerator.java]

nameclass, %method, %block, %line, %
SelectQueryMethodGenerator.java100% (1/1)80%  (4/5)98%  (1060/1087)99%  (297/301)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SelectQueryMethodGenerator100% (1/1)80%  (4/5)98%  (1060/1087)99%  (297/301)
SelectQueryMethodGenerator (): void 0%   (0/1)0%   (0/3)0%   (0/1)
addSelectQueryBody (CodeEmitter, QueryObjectGenerator, Mapper): void 100% (1/1)78%  (36/46)78%  (7/9)
addSelectQueryBodyWithCollection (CodeEmitter, QueryObjectGenerator, Mapper):... 100% (1/1)98%  (756/770)100% (219/220)
addSelectQueryBodyNoCollection (CodeEmitter, QueryObjectGenerator, Mapper): void 100% (1/1)100% (216/216)100% (58/58)
pushSql (CodeEmitter, Mapper, String): void 100% (1/1)100% (52/52)100% (13/13)

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.EXCEPTION_EMPTY_RESULT;
22import static sf.qof.codegen.Constants.EXCEPTION_MORE_THAN_ONE_RESULT;
23import static sf.qof.codegen.Constants.FIELD_NAME_FETCH_SIZE;
24import static sf.qof.codegen.Constants.FIELD_NAME_FIRST_RESULT;
25import static sf.qof.codegen.Constants.FIELD_NAME_MAX_RESULTS;
26import static sf.qof.codegen.Constants.SIG_add;
27import static sf.qof.codegen.Constants.SIG_executeQuery;
28import static sf.qof.codegen.Constants.SIG_getConnection;
29import static sf.qof.codegen.Constants.SIG_next;
30import static sf.qof.codegen.Constants.SIG_prepareStatement;
31import static sf.qof.codegen.Constants.SIG_put;
32import static sf.qof.codegen.Constants.SIG_setFetchSize;
33import static sf.qof.codegen.Constants.SIG_setInt;
34import static sf.qof.codegen.Constants.TYPE_Collection;
35import static sf.qof.codegen.Constants.TYPE_Connection;
36import static sf.qof.codegen.Constants.TYPE_Map;
37import static sf.qof.codegen.Constants.TYPE_PreparedStatement;
38import static sf.qof.codegen.Constants.TYPE_ResultSet;
39import static sf.qof.codegen.Constants.TYPE_SQLException;
40import static sf.qof.codegen.Constants.TYPE_Throwable;
41import static sf.qof.codegen.Constants.TYPE_int;
42 
43import java.util.Collection;
44import java.util.List;
45import java.util.Map;
46import java.util.Set;
47 
48import net.sf.cglib.core.Block;
49import net.sf.cglib.core.CodeEmitter;
50import net.sf.cglib.core.Local;
51import net.sf.cglib.core.Signature;
52 
53import org.objectweb.asm.Label;
54import org.objectweb.asm.Type;
55 
56import sf.qof.customizer.Customizer;
57import sf.qof.dialect.SQLDialect;
58import sf.qof.exception.ValidationException;
59import sf.qof.mapping.Mapper;
60import sf.qof.mapping.ParameterMapping;
61import sf.qof.mapping.ResultMapping;
62import sf.qof.util.InClauseParameterReplacer;
63 
64/**
65 * Internal - SelectQueryMethodGenerator is the main generator class for select query methods.
66 */
67public class SelectQueryMethodGenerator {
68 
69  public static void addSelectQueryBody(CodeEmitter co, QueryObjectGenerator generator, Mapper mapper) {
70    if (mapper.getResults() == null || mapper.getResults().size() == 0) {
71      throw new ValidationException("No result mappers defined");
72    }
73    ResultMapping resultMapping = mapper.getResults().get(0);
74    if (resultMapping.usesCollection()) {
75      // check collection is List
76     if (!Collection.class.isAssignableFrom(resultMapping.getCollectionType())
77      && !Map.class.isAssignableFrom(resultMapping.getCollectionType())) {
78        throw new ValidationException("Return collection type must be of type Collection or Map");
79      }
80      addSelectQueryBodyWithCollection(co, generator, mapper);
81    } else {
82      addSelectQueryBodyNoCollection(co, generator, mapper);
83    }
84  }
85 
86  private static void addSelectQueryBodyNoCollection(CodeEmitter co, QueryObjectGenerator generator, Mapper mapper) {
87    Local localPreparedStatement = co.make_local(TYPE_PreparedStatement);
88    Local localResultSet = co.make_local(TYPE_ResultSet);
89    Local localException = co.make_local(TYPE_Throwable);
90  
91    // ResultSet rs = null;
92    co.aconst_null();
93    co.store_local(localResultSet);
94  
95    // ps = connection.prepareStatement(sql);
96    co.load_this();
97    co.invoke_virtual(Type.getType(generator.getClassNameType()), SIG_getConnection);
98    co.push(mapper.getSql());
99    co.invoke_interface(TYPE_Connection, SIG_prepareStatement);
100    co.store_local(localPreparedStatement);
101  
102    // try{
103    Block blockTry = co.begin_block();
104  
105    // set fetch size to two as we do not expect more than one row
106    // ps.setFetchSize(2);
107    co.load_local(localPreparedStatement);
108    co.push(2);
109    co.invoke_interface(TYPE_PreparedStatement, SIG_setFetchSize);
110  
111    ParameterMappingGenerator pmg = new ParameterMappingGenerator(co, localPreparedStatement, null, null, null);
112    mapper.acceptParameterMappers(pmg);
113  
114    // ps.executeQuery();
115    co.load_local(localPreparedStatement);
116    co.invoke_interface(TYPE_PreparedStatement, SIG_executeQuery);
117    co.store_local(localResultSet);
118  
119    Class<?> returnType = mapper.getMethod().getReturnInfo().getType();
120  
121    Label labelThrowNoResult = co.make_label();
122    Label labelThrowMoreThanOneResult = co.make_label();
123    Label labelFinally = co.make_label();
124  
125    Local localResult = null;
126  
127    localResult = co.make_local(Type.getType(returnType));
128    if (!returnType.isPrimitive()) {
129      co.aconst_null();
130      co.store_local(localResult);
131    }
132    // if (rs.next()) {
133    co.load_local(localResultSet);
134    co.invoke_interface(TYPE_ResultSet, SIG_next);
135    if (returnType.isPrimitive()) {
136      co.if_jump(CodeEmitter.EQ, labelThrowNoResult);
137    } else {
138      co.if_jump(CodeEmitter.EQ, labelFinally);
139    }
140  
141    if (!(mapper.getResults().size() == 1 && mapper.getResults().get(0).usesAtomic())) {
142      EmitUtils.createAndStoreNewResultObject(co, mapper, localResultSet, localResult);
143    }
144  
145    // --- get results
146    ResultMappingGenerator rmp = new ResultMappingGenerator(co, localResultSet, localResult, null, false, null);
147    mapper.acceptResultMappers(rmp);
148  
149    // if (rs.next()) {
150    co.load_local(localResultSet);
151    co.invoke_interface(TYPE_ResultSet, SIG_next);
152    co.if_jump(CodeEmitter.NE, labelThrowMoreThanOneResult);
153    co.goTo(labelFinally);
154  
155    co.mark(labelThrowMoreThanOneResult);
156    // throw new SQLException("More than one result in result set");
157    co.throw_exception(TYPE_SQLException, EXCEPTION_MORE_THAN_ONE_RESULT);
158    // throw new SQLException("Empty result set returned");
159    co.mark(labelThrowNoResult);
160    co.throw_exception(TYPE_SQLException, EXCEPTION_EMPTY_RESULT);
161  
162    // finally block
163    co.mark(labelFinally);
164    EmitUtils.emitClose(co, localResultSet, true);
165    EmitUtils.emitClose(co, localPreparedStatement, false);
166  
167    // return result
168    co.load_local(localResult);
169    co.return_value();
170    // }
171  
172    // exception handler + finally
173    blockTry.end();
174    co.catch_exception(blockTry, TYPE_Throwable);
175  
176    // store thrown exception
177    co.store_local(localException);
178  
179    // finally block
180    EmitUtils.emitClose(co, localResultSet, true);
181    EmitUtils.emitClose(co, localPreparedStatement, false);
182  
183    // throw stored exception
184    co.load_local(localException);
185    co.athrow();
186  }
187 
188  private static void addSelectQueryBodyWithCollection(CodeEmitter co, QueryObjectGenerator generator, Mapper mapper) {
189    Local localPreparedStatement = co.make_local(TYPE_PreparedStatement);
190    Local localResultSet = co.make_local(TYPE_ResultSet);
191    Local localResultCollection = co.make_local(TYPE_Collection);
192    Local localException = co.make_local(TYPE_Throwable);
193    Local localMapKey;
194    boolean usesMap = false;
195    
196    SQLDialect sqlDialect = generator.getSqlDialect();
197    boolean implementPaging = generator.getImplementPaging();
198    String classNameType = generator.getClassNameType();
199    Customizer customizer = generator.getCustomizer();
200    
201    Class<?> resultMapKeyType = mapper.getMethod().getReturnInfo().getMapKeyType();
202    if (resultMapKeyType == null) {
203      localMapKey = null;
204    } else {
205      localMapKey = co.make_local(Type.getType(resultMapKeyType));
206    }
207    
208    // ResultSet rs = null;
209    co.aconst_null();
210    co.store_local(localResultSet);
211    // list = new ArrayList();
212    Type collectionType = null;
213    Class<?> resultCollectionType = mapper.getMethod().getReturnInfo().getCollectionType();
214    if (resultCollectionType == List.class) {
215            collectionType = customizer.getListType();
216    } else if (resultCollectionType == Set.class) {
217            collectionType = customizer.getSetType();
218    } else if (resultCollectionType == Map.class) {
219      usesMap = true;
220      collectionType = customizer.getMapType();
221    } else {
222            throw new ValidationException("Collection type " + resultCollectionType + " is not allowed");
223    }
224    co.new_instance(collectionType);
225    co.dup();
226    co.invoke_constructor(collectionType);
227    co.store_local(localResultCollection);
228  
229    Local localParameterIndexOffset = null;
230    if (mapper.usesArray() || 
231        (implementPaging && sqlDialect.limitParametersBeforeQueryParameters())) {
232      localParameterIndexOffset = co.make_local(TYPE_int);
233    }
234    if (mapper.usesArray()) {
235      co.push(0);
236      co.store_local(localParameterIndexOffset);
237    }
238  
239    if (implementPaging) {
240      String sql = mapper.getSql();
241      
242      Label label1 = co.make_label();
243      Label label2 = co.make_label();
244      Label label3 = co.make_label();
245      co.load_this();
246      co.getfield(FIELD_NAME_FIRST_RESULT);
247      co.if_jump(CodeEmitter.NE, label1);
248      co.load_this();
249      co.getfield(FIELD_NAME_MAX_RESULTS);
250      co.if_jump(CodeEmitter.NE, label1);
251      
252      co.load_this();
253      co.invoke_virtual(Type.getType(classNameType), SIG_getConnection);
254      //co.push(sql);
255      pushSql(co, mapper, sql);
256      co.invoke_interface(TYPE_Connection, SIG_prepareStatement);
257      co.store_local(localPreparedStatement);
258      
259      if (sqlDialect.limitParametersBeforeQueryParameters()) {
260            co.push(0);
261            co.store_local(localParameterIndexOffset);
262      }
263  
264      co.goTo(label2);
265      
266      co.mark(label1);
267      co.load_this();
268      co.getfield(FIELD_NAME_FIRST_RESULT);
269      co.if_jump(CodeEmitter.NE, label3);
270  
271      co.load_this();
272      co.invoke_virtual(Type.getType(classNameType), SIG_getConnection);
273      //co.push(sqlDialect.getLimitString(sql, false));
274      pushSql(co, mapper, sqlDialect.getLimitString(sql, false));
275      co.invoke_interface(TYPE_Connection, SIG_prepareStatement);
276      co.store_local(localPreparedStatement);
277  
278      if (sqlDialect.limitParametersBeforeQueryParameters()) {
279        co.push(1);
280        co.store_local(localParameterIndexOffset);
281        
282        co.load_local(localPreparedStatement);
283        co.push(1);
284        co.load_this();
285        co.getfield(FIELD_NAME_MAX_RESULTS);
286        co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
287        
288      } else {
289        co.load_local(localPreparedStatement);
290        co.push(1 + mapper.getMaxParameterSqlIndex());
291        co.load_this();
292        co.getfield(FIELD_NAME_MAX_RESULTS);
293        co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
294      }
295      co.goTo(label2);
296  
297      co.mark(label3);
298  
299      co.load_this();
300      co.invoke_virtual(Type.getType(classNameType), SIG_getConnection);
301      // co.push(sqlDialect.getLimitString(sql, true));
302      pushSql(co, mapper, sqlDialect.getLimitString(sql, true));
303      co.invoke_interface(TYPE_Connection, SIG_prepareStatement);
304      co.store_local(localPreparedStatement);
305  
306      if (sqlDialect.limitParametersBeforeQueryParameters()) {
307        co.push(2);
308        co.store_local(localParameterIndexOffset);
309  
310        if (sqlDialect.limitOffsetFirst()) {
311          co.load_local(localPreparedStatement);
312          co.push(1);
313          co.load_this();
314          co.getfield(FIELD_NAME_FIRST_RESULT);
315          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
316  
317          co.load_local(localPreparedStatement);
318          co.push(2);
319          if (sqlDialect.limitAddOffset()) {
320            co.load_this();
321            co.getfield(FIELD_NAME_FIRST_RESULT);
322            co.load_this();
323            co.getfield(FIELD_NAME_MAX_RESULTS);
324            co.math(CodeEmitter.ADD, TYPE_int);
325          } else {
326            co.load_this();
327            co.getfield(FIELD_NAME_MAX_RESULTS);
328          }
329          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
330        } else {
331          co.load_local(localPreparedStatement);
332          co.push(1);
333          if (sqlDialect.limitAddOffset()) {
334            co.load_this();
335            co.getfield(FIELD_NAME_FIRST_RESULT);
336            co.load_this();
337            co.getfield(FIELD_NAME_MAX_RESULTS);
338            co.math(CodeEmitter.ADD, TYPE_int);
339          } else {
340            co.load_this();
341            co.getfield(FIELD_NAME_MAX_RESULTS);
342          }
343          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
344  
345          co.load_local(localPreparedStatement);
346          co.push(2);
347          co.load_this();
348          co.getfield(FIELD_NAME_FIRST_RESULT);
349          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
350        }
351      } else {
352        if (sqlDialect.limitOffsetFirst()) {
353          co.load_local(localPreparedStatement);
354          co.push(1 + mapper.getMaxParameterSqlIndex());
355          co.load_this();
356          co.getfield(FIELD_NAME_FIRST_RESULT);
357          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
358  
359          if (sqlDialect.limitAddOffset()) {
360            co.load_local(localPreparedStatement);
361            co.push(2 + mapper.getMaxParameterSqlIndex());
362            co.load_this();
363            co.getfield(FIELD_NAME_FIRST_RESULT);
364            co.load_this();
365            co.getfield(FIELD_NAME_MAX_RESULTS);
366            co.math(CodeEmitter.ADD, TYPE_int);
367          } else {
368            co.load_local(localPreparedStatement);
369            co.push(2 + mapper.getMaxParameterSqlIndex());
370            co.load_this();
371            co.getfield(FIELD_NAME_MAX_RESULTS);
372          }
373          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
374        } else {
375          if (sqlDialect.limitAddOffset()) {
376            co.load_local(localPreparedStatement);
377            co.push(1 + mapper.getMaxParameterSqlIndex());
378            co.load_this();
379            co.getfield(FIELD_NAME_FIRST_RESULT);
380            co.load_this();
381            co.getfield(FIELD_NAME_MAX_RESULTS);
382            co.math(CodeEmitter.ADD, TYPE_int);
383          } else {
384            co.load_local(localPreparedStatement);
385            co.push(1 + mapper.getMaxParameterSqlIndex());
386            co.load_this();
387            co.getfield(FIELD_NAME_MAX_RESULTS);
388          }
389          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
390  
391          co.load_local(localPreparedStatement);
392          co.push(2 + mapper.getMaxParameterSqlIndex());
393          co.load_this();
394          co.getfield(FIELD_NAME_FIRST_RESULT);
395          co.invoke_interface(TYPE_PreparedStatement, SIG_setInt);
396        }
397      }
398      
399      co.mark(label2);
400          
401      // reset firstResult, maxResults
402  
403      // firstResult = 0;
404      co.load_this();
405      co.push(0);
406      co.putfield(FIELD_NAME_FIRST_RESULT);
407      // maxResults = 0;
408      co.load_this();
409      co.push(0);
410      co.putfield(FIELD_NAME_MAX_RESULTS);
411  
412    } else {
413      // ps = connection.prepareStatement(sql);
414      co.load_this();
415      co.invoke_virtual(Type.getType(classNameType), SIG_getConnection);
416      pushSql(co, mapper, mapper.getSql());
417      co.invoke_interface(TYPE_Connection, SIG_prepareStatement);
418      co.store_local(localPreparedStatement);
419    }
420  
421    // try{
422    Block blockTry = co.begin_block();
423  
424    // ps.setFetchSize(fetchSize);
425    co.load_local(localPreparedStatement);
426    co.load_this();
427    generator.emitGetField(co, FIELD_NAME_FETCH_SIZE);
428    co.invoke_interface(TYPE_PreparedStatement, SIG_setFetchSize);
429  
430    ParameterMappingGenerator pmg = new ParameterMappingGenerator(co, localPreparedStatement, 
431            null, null, localParameterIndexOffset);
432    mapper.acceptParameterMappers(pmg);
433  
434    // ps.executeQuery();
435    co.load_local(localPreparedStatement);
436    co.invoke_interface(TYPE_PreparedStatement, SIG_executeQuery);
437    co.store_local(localResultSet);
438  
439    ResultMapping resultMapping = null;
440    for (ResultMapping rm : mapper.getResults()) {
441      if (!rm.isMapKey()) {
442        resultMapping = rm;
443        break;
444      }
445    }
446  
447    Label labelWhile = co.make_label();
448    Label labelFinally = co.make_label();
449  
450    Local localResult = co.make_local(Type.getType(resultMapping.getBeanType() != null ? resultMapping
451        .getBeanType() : resultMapping.getType()));
452    // while (rs.next()) {
453    co.mark(labelWhile);
454    co.load_local(localResultSet);
455    co.invoke_interface(TYPE_ResultSet, SIG_next);
456    co.if_jump(CodeEmitter.EQ, labelFinally);
457  
458    if (!(mapper.getResults().size() == 1 && resultMapping.usesAtomic())
459        && !(usesMap && mapper.getResults().size() == 2 && resultMapping.usesAtomic())) {
460      EmitUtils.createAndStoreNewResultObject(co, mapper, localResultSet, localResult);
461    }
462  
463    // get results
464    ResultMappingGenerator rmp = new ResultMappingGenerator(co, localResultSet, localResult, localMapKey, false, null);
465    mapper.acceptResultMappers(rmp);
466  
467    if (usesMap) {
468      co.load_local(localResultCollection);
469      co.load_local(localMapKey);
470      co.load_local(localResult);
471      co.invoke_interface(TYPE_Map, SIG_put);
472      co.pop();
473    } else {
474      // add result
475      co.load_local(localResultCollection);
476      co.load_local(localResult);
477      co.invoke_interface(TYPE_Collection, SIG_add);
478      co.pop();
479    }
480  
481    // } // end while
482    co.goTo(labelWhile);
483  
484    // finally block
485    co.mark(labelFinally);
486    EmitUtils.emitClose(co, localResultSet, true);
487    EmitUtils.emitClose(co, localPreparedStatement, false);
488  
489    // return result
490    co.load_local(localResultCollection);
491    co.return_value();
492    // }
493  
494    // exception handler + finally
495    blockTry.end();
496    co.catch_exception(blockTry, TYPE_Throwable);
497  
498    // store thrown exception
499    co.store_local(localException);
500  
501    // finally block
502    EmitUtils.emitClose(co, localResultSet, true);
503    EmitUtils.emitClose(co, localPreparedStatement, false);
504  
505    // throw stored exception
506    co.load_local(localException);
507    co.athrow();
508  }
509 
510  private static void pushSql(CodeEmitter co, Mapper mapper, String sql) {
511    if (mapper.usesArray()) {
512      co.push(sql);
513      List<ParameterMapping> mappings = mapper.getParameters(); 
514      for (int i = mappings.size() - 1; i >= 0; i--) {
515        ParameterMapping mapping = mappings.get(i);
516        if (mapping.usesArray()) {
517          co.push(mapping.getSqlIndexes()[0]); // index
518          co.load_arg(mapping.getIndex());
519          co.arraylength(); // numArgs
520          co.invoke_static(Type.getType(InClauseParameterReplacer.class), 
521              new Signature("replace", "(Ljava/lang/String;II)Ljava/lang/String;"));
522        }
523      }
524    } else {
525      co.push(sql);
526    }
527  }
528 
529}

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