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

COVERAGE SUMMARY FOR SOURCE FILE [AnnotationMapperFactory.java]

nameclass, %method, %block, %line, %
AnnotationMapperFactory.java100% (2/2)92%  (11/12)95%  (688/726)95%  (143.2/151)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AnnotationMapperFactory100% (1/1)90%  (9/10)95%  (679/717)95%  (142.2/150)
AnnotationMapperFactory (): void 0%   (0/1)0%   (0/3)0%   (0/1)
findConstructor (MethodReturnInfo, ResultDefinition []): Constructor 100% (1/1)91%  (193/212)93%  (45.4/49)
createResultMappers (MethodInfo, ResultDefinition []): List 100% (1/1)95%  (160/169)95%  (38/40)
createParameterMappers (MethodInfo, ParameterDefinition []): List 100% (1/1)96%  (160/167)97%  (36.8/38)
create (Method): Mapper 100% (1/1)100% (60/60)100% (12/12)
create (MethodInfo, Call): Mapper 100% (1/1)100% (23/23)100% (2/2)
create (MethodInfo, Delete): Mapper 100% (1/1)100% (20/20)100% (2/2)
create (MethodInfo, Insert): Mapper 100% (1/1)100% (20/20)100% (2/2)
create (MethodInfo, Query): Mapper 100% (1/1)100% (23/23)100% (2/2)
create (MethodInfo, Update): Mapper 100% (1/1)100% (20/20)100% (2/2)
     
class AnnotationMapperFactory$1100% (1/1)100% (2/2)100% (9/9)100% (2/2)
AnnotationMapperFactory$1 (): void 100% (1/1)100% (3/3)100% (1/1)
compare (ResultDefinition, ResultDefinition): int 100% (1/1)100% (6/6)100% (1/1)

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 java.lang.reflect.Constructor;
22import java.lang.reflect.Method;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.Comparator;
26import java.util.List;
27 
28import sf.qof.Call;
29import sf.qof.Delete;
30import sf.qof.Insert;
31import sf.qof.Query;
32import sf.qof.Update;
33import sf.qof.exception.ValidationException;
34import sf.qof.mapping.Mapper;
35import sf.qof.mapping.MappingFactory;
36import sf.qof.mapping.MethodInfo;
37import sf.qof.mapping.MethodInfoFactory;
38import sf.qof.mapping.MethodParameterInfo;
39import sf.qof.mapping.MethodReturnInfo;
40import sf.qof.mapping.ParameterMapping;
41import sf.qof.mapping.QueryType;
42import sf.qof.mapping.ResultMapping;
43import sf.qof.mapping.MappingFactory.MappingClassInfo;
44import sf.qof.parser.ParameterDefinition;
45import sf.qof.parser.ResultDefinition;
46import sf.qof.parser.SqlParser;
47import sf.qof.util.ReflectionUtils;
48 
49/**
50 * Internal - AnnotationMapperFactory creates mappings from annotations and 
51 * method definitions.
52 */
53public final class AnnotationMapperFactory {
54 
55  private AnnotationMapperFactory() { }
56  
57  public static Mapper create(Method method) {
58        MethodInfo methodInfo = MethodInfoFactory.createMethodInfo(method);
59    if (method.isAnnotationPresent(Query.class)) {
60      return create(methodInfo, method.getAnnotation(Query.class));
61    } else if (method.isAnnotationPresent(Insert.class)) {
62      return create(methodInfo, method.getAnnotation(Insert.class));
63    } else if (method.isAnnotationPresent(Update.class)) {
64      return create(methodInfo, method.getAnnotation(Update.class));
65    } else if (method.isAnnotationPresent(Delete.class)) {
66      return create(methodInfo, method.getAnnotation(Delete.class));
67    } else if (method.isAnnotationPresent(Call.class)) {
68      return create(methodInfo, method.getAnnotation(Call.class));
69    } else {
70      return null;
71    }
72  }
73 
74  private static Mapper create(MethodInfo methodInfo, Query annotation) {
75    SqlParser parser = new SqlParser(annotation.sql(), false);
76    return new Mapper(methodInfo, QueryType.QUERY, parser.getSql(),
77        createParameterMappers(methodInfo, parser.getParameterDefinitions()),
78        createResultMappers(methodInfo, parser.getResultDefinitions()));
79  }
80 
81  private static Mapper create(MethodInfo methodInfo, Insert annotation) {
82    SqlParser parser = new SqlParser(annotation.sql(), false);
83    return new Mapper(methodInfo, QueryType.INSERT, parser.getSql(),
84        createParameterMappers(methodInfo, parser.getParameterDefinitions()), null);
85  }
86 
87  private static Mapper create(MethodInfo methodInfo, Update annotation) {
88    SqlParser parser = new SqlParser(annotation.sql(), false);
89    return new Mapper(methodInfo, QueryType.UPDATE, parser.getSql(), 
90        createParameterMappers(methodInfo, parser.getParameterDefinitions()), null);
91  }
92 
93  private static Mapper create(MethodInfo methodInfo, Delete annotation) {
94    SqlParser parser = new SqlParser(annotation.sql(), false);
95    return new Mapper(methodInfo, QueryType.DELETE, parser.getSql(), 
96        createParameterMappers(methodInfo, parser.getParameterDefinitions()), null);
97  }
98 
99  private static Mapper create(MethodInfo methodInfo, Call annotation) {
100    SqlParser parser = new SqlParser(annotation.sql(), true);
101    return new Mapper(methodInfo, QueryType.CALL, parser.getSql(), 
102        createParameterMappers(methodInfo, parser.getParameterDefinitions()),
103        createResultMappers(methodInfo, parser.getResultDefinitions()));
104  }
105 
106  private static List<ParameterMapping> createParameterMappers(MethodInfo methodInfo, ParameterDefinition[] parameterDefs) {
107    List<ParameterMapping> list = new ArrayList<ParameterMapping>();
108    for (ParameterDefinition parameter : parameterDefs) {
109      // get fields from annotation
110      String mappingType = parameter.getType();
111      int index = parameter.getParameter() - 1;
112      if (index < 0 || index >= methodInfo.getParameterInfos().length) {
113        throw new ValidationException("Invalid parameter index for method " + methodInfo);
114      }
115      int[] sqlIndexes = parameter.getIndexes();
116      String[] sqlColumns = parameter.getNames();
117      if (sqlIndexes == null && sqlColumns == null) {
118        throw new ValidationException("Either indexes or columns must be defined");
119      }
120      String field = parameter.getField();
121      Class<?> type = null;
122      Class<?> collectionType = null;
123      Class<?> collectionElementType = null;
124      Class<?> arrayElementType = null;
125      Class<?> beanType = null;
126      Method getter = null;
127 
128      // get types
129      MethodParameterInfo parameterInfo = methodInfo.getParameterInfos()[index];
130      collectionType = parameterInfo.getCollectionType();
131      collectionElementType = parameterInfo.getCollectionElementType();
132      arrayElementType = parameterInfo.getArrayElementType();
133      
134      boolean usesArray = arrayElementType != null && arrayElementType != Byte.TYPE;
135      if (field == null) {
136        if (collectionType != null) {
137          type = collectionElementType;
138        } else if (usesArray) {
139          type = arrayElementType;
140        } else {
141          type = parameterInfo.getType();
142        }
143      } else {
144        if (collectionType != null) {
145          beanType = collectionElementType;
146        } else {
147          beanType = parameterInfo.getType();
148        }
149        getter = ReflectionUtils.findGetter(beanType, field);
150        if (getter == null) {
151          throw new ValidationException("Cannot find or access getter for " + field + " in class " + beanType.getName());
152        }
153        type = getter.getReturnType();
154      }
155      // create mapping
156      ParameterMapping mapping = MappingFactory.createParameterMapping(mappingType, index, type, collectionType,
157          beanType, getter, sqlIndexes, sqlColumns, usesArray);
158      list.add(mapping);
159    }
160    return list;
161  }
162 
163  private static List<ResultMapping> createResultMappers(MethodInfo methodInfo, ResultDefinition[] resultDefs) {
164    List<ResultMapping> list = new ArrayList<ResultMapping>();
165    
166    MethodReturnInfo returnInfo = methodInfo.getReturnInfo();
167    Constructor<?> constructor = findConstructor(returnInfo, resultDefs);
168    
169    for (ResultDefinition result : resultDefs) {
170      // get fields from annotation
171      String mappingType = result.getType();
172      int[] sqlIndexes = result.getIndexes();
173      String[] sqlColumns = result.getColumns();
174      if (sqlIndexes == null && sqlColumns == null) {
175        throw new ValidationException("Either indexes or columns must be defined");
176      }
177      Integer constructorParameter = null;
178      if (result.getConstructorParameter() > 0) {
179            constructorParameter = result.getConstructorParameter();
180      }
181      String field = result.getField();
182      Class<?> type = null;
183      Class<?> collectionType = returnInfo.getCollectionType();
184      Class<?> collectionElementType = returnInfo.getCollectionElementType();
185      Class<?> mapKeyType = null;
186      Class<?> beanType = null;
187      Method setter = null;
188 
189      // get types
190      
191      if (field == null) {
192        if (collectionType != null) {
193          type = collectionElementType;
194          if (result.isMapKey()) {
195            mapKeyType = returnInfo.getMapKeyType();
196          }
197        } else {
198          type = returnInfo.getType();
199        }
200      } else {
201        if (collectionType != null) {
202          beanType = returnInfo.getCollectionElementType();
203          if (result.isMapKey()) {
204            mapKeyType = returnInfo.getMapKeyType();
205          }
206        } else {
207          beanType = returnInfo.getType();
208        }
209        MappingClassInfo info = MappingFactory.getResultMappingInfo(mappingType);
210        if (info == null) {
211          setter = ReflectionUtils.findSetter(beanType, field);
212        } else {
213          setter = ReflectionUtils.findSetter(beanType, field, info.getMappableTypes());
214        }
215        if (setter == null) {
216          throw new ValidationException("Cannot find or access setter for " + field + " in class " + beanType.getName()
217              + " for mapping type " + mappingType);
218        }
219        type = setter.getParameterTypes()[0];
220      }
221 
222      // create mapping
223      ResultMapping mapping = MappingFactory.createResultMapping(mappingType, type, collectionType, beanType, setter,
224          sqlIndexes, sqlColumns, mapKeyType, constructorParameter, (constructorParameter != null ? constructor : null));
225      list.add(mapping);
226    }
227    return list;
228  }
229 
230  private static Constructor<?> findConstructor(MethodReturnInfo returnInfo, ResultDefinition[] resultDefs) {
231    List<ResultDefinition> constructorResultDefs = new ArrayList<ResultDefinition>();
232    for (ResultDefinition resultDef : resultDefs) {
233      if (resultDef.getConstructorParameter() > 0) {
234        constructorResultDefs.add(resultDef);
235      }
236    }
237    if (constructorResultDefs.size() == 0) {
238      return null;
239    }
240    Collections.sort(constructorResultDefs, new Comparator<ResultDefinition>() {
241      public int compare(ResultDefinition o1, ResultDefinition o2) {
242        return o1.getConstructorParameter() - o2.getConstructorParameter();
243      }
244    });
245 
246    Class<?> type;
247    if (returnInfo.getCollectionType() != null) {
248      type = returnInfo.getCollectionElementType();
249    } else {
250      type = returnInfo.getType();
251    }
252 
253    Constructor<?>[] constructors = type.getConstructors();
254    
255    Constructor<?> matchingConstructor = null;
256    for (Constructor<?> constructor : constructors) {
257      Class<?>[] parameterTypes = constructor.getParameterTypes();
258      if (parameterTypes.length != constructorResultDefs.size()) {
259        continue;
260      }
261      for (int i = 0; i < parameterTypes.length; i++) {
262        ResultDefinition def = constructorResultDefs.get(i);
263        MappingClassInfo info = MappingFactory.getResultMappingInfo(def.getType());
264        if (!info.getMappableTypes().contains(parameterTypes[i])) {
265          break;
266        }
267        if (i + 1 == parameterTypes.length) {
268          matchingConstructor = constructor;
269          break;
270        }
271      }
272      if (matchingConstructor != null) {
273        break;
274      }
275    }
276 
277    if (matchingConstructor == null) {
278      // try again but relaxed rules
279      for (Constructor<?> constructor : constructors) {
280        Class<?>[] parameterTypes = constructor.getParameterTypes();
281        if (parameterTypes.length != constructorResultDefs.size()) {
282          continue;
283        }
284        for (int i = 0; i < parameterTypes.length; i++) {
285          ResultDefinition def = constructorResultDefs.get(i);
286          MappingClassInfo info = MappingFactory.getResultMappingInfo(def.getType());
287          boolean found = false;
288          for (Class<?> mappableType : info.getMappableTypes()) {
289            if (mappableType.isAssignableFrom(parameterTypes[i])) {
290              found = true;
291              break;
292            }
293          }
294          if (!found) {
295            break;
296          }
297          if (i + 1 == parameterTypes.length) {
298            matchingConstructor = constructor;
299            break;
300          }
301        }
302        if (matchingConstructor != null) {
303          break;
304        }
305      }
306    }
307    
308    if (matchingConstructor == null) {
309      throw new RuntimeException("Could not find matching constructor in " + type);
310    }
311    
312    return matchingConstructor;
313  }
314}

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