Coverage Summary for Class: RecordInfo (net.sf.persism)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| RecordInfo | 100% (1/1) | 100% (6/6) | 100% (50/50) |
1 package net.sf.persism; 2 3 import java.beans.ConstructorProperties; 4 import java.lang.reflect.Constructor; 5 import java.lang.reflect.Parameter; 6 import java.sql.ResultSet; 7 import java.sql.ResultSetMetaData; 8 import java.sql.SQLException; 9 import java.util.*; 10 import java.util.stream.Collectors; 11 12 import static net.sf.persism.Util.listEqualsIgnoreOrder; 13 14 // todo Cache RecordInfo 15 class RecordInfo<T> { 16 17 private final Map<String, PropertyInfo> propertyInfoByConstructorOrder; 18 private final Constructor<T> constructor; 19 private final Map<String, Integer> ordinals; 20 21 public RecordInfo(Class<T> objectClass, Map<String, PropertyInfo> properties, ResultSet rs) throws SQLException { 22 // resultset may not have columns in the proper order 23 // resultset may not have all columns 24 // get column order based on which properties are found 25 // get matching constructor 26 27 List<String> propertyNames = new ArrayList<>(properties.values().stream().map(propertyInfo -> propertyInfo.propertyName).toList()); 28 Constructor<T> selectedConstructor = findConstructor(objectClass, propertyNames); 29 30 // now re-arrange by property order 31 propertyInfoByConstructorOrder = new LinkedHashMap<>(selectedConstructor.getParameterCount()); 32 for (String paramName : propertyNames) { 33 for (String col : properties.keySet()) { 34 if (paramName.equals(properties.get(col).field.getName())) { 35 propertyInfoByConstructorOrder.put(col, properties.get(col)); 36 } 37 } 38 } 39 40 List<Class<?>> constructorTypes = new ArrayList<>(12); 41 // put into ordinals the order to read from to match the constructor 42 ResultSetMetaData rsmd = rs.getMetaData(); 43 ordinals = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 44 for (int j = 1; j <= rsmd.getColumnCount(); j++) { 45 ordinals.put(rsmd.getColumnLabel(j), j); 46 } 47 48 for (String col : propertyInfoByConstructorOrder.keySet()) { 49 if (ordinals.containsKey(col)) { 50 constructorTypes.add(propertyInfoByConstructorOrder.get(col).field.getType()); 51 } else { 52 // can happen if a user manually constructs the SQL and misses a column 53 throw new PersismException(Message.ReadRecordColumnNotFound.message(objectClass, col)); 54 } 55 } 56 57 try { 58 constructor = objectClass.getConstructor(constructorTypes.toArray(new Class<?>[0])); 59 assert constructor.equals(selectedConstructor); 60 } catch (NoSuchMethodException e) { 61 throw new PersismException(Message.ReadRecordCouldNotInstantiate.message(objectClass, constructorTypes)); 62 } 63 } 64 65 private Constructor<T> findConstructor(Class<T> objectClass, List<String> propertyNames) { 66 67 //noinspection unchecked 68 Constructor<T>[] constructors = (Constructor<T>[]) objectClass.getConstructors(); 69 Constructor<T> selectedConstructor = null; 70 71 for (Constructor<T> constructor : constructors) { 72 // Check with canonical or maybe -parameters 73 List<String> parameterNames = Arrays.stream(constructor.getParameters()). 74 map(Parameter::getName).collect(Collectors.toList()); 75 76 // why don't I just use the parameterNames instead of modifying property list? 77 if (listEqualsIgnoreOrder(propertyNames, parameterNames)) { 78 // re-arrange the propertyNames to match parameterNames 79 propertyNames.clear(); 80 propertyNames.addAll(parameterNames); 81 selectedConstructor = constructor; 82 break; 83 } 84 85 // Check with ConstructorProperties 86 ConstructorProperties constructorProperties = constructor.getAnnotation(ConstructorProperties.class); 87 if (constructorProperties != null) { 88 parameterNames = Arrays.asList(constructorProperties.value()); 89 if (listEqualsIgnoreOrder(propertyNames, parameterNames)) { 90 // re-arrange the propertyNames to match parameterNames 91 propertyNames.clear(); 92 propertyNames.addAll(parameterNames); 93 selectedConstructor = constructor; 94 break; 95 } 96 } 97 } 98 99 if (selectedConstructor == null) { 100 throw new PersismException(Message.CouldNotFindConstructorForRecord.message(objectClass.getName(), propertyNames)); 101 } 102 return selectedConstructor; 103 104 } 105 106 public Map<String, PropertyInfo> propertyInfoByConstructorOrder() { 107 return propertyInfoByConstructorOrder; 108 } 109 110 public Constructor<T> constructor() { 111 return constructor; 112 } 113 114 public Map<String, Integer> ordinals() { 115 return ordinals; 116 } 117 118 }