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 }