Coverage Summary for Class: RecordInfo (net.sf.persism)

Class Class, % Method, % Branch, % Line, %
RecordInfo 100% (1/1) 100% (6/6) 95.8% (23/24) 100% (43/43)


 package net.sf.persism;
 
 import java.beans.ConstructorProperties;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Parameter;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.util.*;
 import java.util.stream.Collectors;
 
 import static net.sf.persism.Util.listEqualsIgnoreOrder;
 
 // todo Cache RecordInfo
 class RecordInfo<T> {
 
     private final Map<String, PropertyInfo> propertyInfoByConstructorOrder;
     private final Constructor<T> constructor;
     private final Map<String, Integer> ordinals;
 
     public RecordInfo(Class<T> objectClass, Map<String, PropertyInfo> properties, ResultSet rs) throws SQLException {
         // resultset may not have columns in the proper order
         // resultset may not have all columns
         // get column order based on which properties are found
         // get matching constructor
 
         List<String> propertyNames = new ArrayList<>(properties.values().stream().map(propertyInfo -> propertyInfo.propertyName).toList());
         Constructor<T> selectedConstructor = findConstructor(objectClass, propertyNames);
 
         // now re-arrange by property order
         propertyInfoByConstructorOrder = new LinkedHashMap<>(selectedConstructor.getParameterCount());
         for (String paramName : propertyNames) {
             for (String col : properties.keySet()) {
                 if (paramName.equals(properties.get(col).field.getName())) {
                     propertyInfoByConstructorOrder.put(col, properties.get(col));
                 }
             }
         }
 
         List<Class<?>> constructorTypes = new ArrayList<>(12);
         // put into ordinals the order to read from to match the constructor
         ResultSetMetaData rsmd = rs.getMetaData();
         ordinals = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
         for (int j = 1; j <= rsmd.getColumnCount(); j++) {
             ordinals.put(rsmd.getColumnLabel(j), j);
         }
 
         for (String col : propertyInfoByConstructorOrder.keySet()) {
             if (ordinals.containsKey(col)) {
                 constructorTypes.add(propertyInfoByConstructorOrder.get(col).field.getType());
             } else {
                 // can happen if a user manually constructs the SQL and misses a column
                 throw new PersismException(Message.ReadRecordColumnNotFound.message(objectClass, col));
             }
         }
 
         try {
             constructor = objectClass.getConstructor(constructorTypes.toArray(new Class<?>[0]));
             assert constructor.equals(selectedConstructor);
         } catch (NoSuchMethodException e) {
             throw new PersismException(Message.ReadRecordCouldNotInstantiate.message(objectClass, constructorTypes));
         }
     }
 
     private Constructor<T> findConstructor(Class<T> objectClass, List<String> propertyNames) {
 
         //noinspection unchecked
         Constructor<T>[] constructors = (Constructor<T>[]) objectClass.getConstructors();
         Constructor<T> selectedConstructor = null;
 
         for (Constructor<T> constructor : constructors) {
             // Check with canonical or maybe -parameters
             List<String> parameterNames = Arrays.stream(constructor.getParameters()).
                     map(Parameter::getName).collect(Collectors.toList());
 
             // why don't I just use the parameterNames instead of modifying property list?
             if (listEqualsIgnoreOrder(propertyNames, parameterNames)) {
                 // re-arrange the propertyNames to match parameterNames
                 propertyNames.clear();
                 propertyNames.addAll(parameterNames);
                 selectedConstructor = constructor;
                 break;
             }
 
             // Check with ConstructorProperties
             ConstructorProperties constructorProperties = constructor.getAnnotation(ConstructorProperties.class);
             if (constructorProperties != null) {
                 parameterNames = Arrays.asList(constructorProperties.value());
                 if (listEqualsIgnoreOrder(propertyNames, parameterNames)) {
                     // re-arrange the propertyNames to match parameterNames
                     propertyNames.clear();
                     propertyNames.addAll(parameterNames);
                     selectedConstructor = constructor;
                     break;
                 }
             }
         }
 
         if (selectedConstructor == null) {
             throw new PersismException(Message.CouldNotFindConstructorForRecord.message(objectClass.getName(), propertyNames));
         }
         return selectedConstructor;
 
     }
 
     public Map<String, PropertyInfo> propertyInfoByConstructorOrder() {
         return propertyInfoByConstructorOrder;
     }
 
     public Constructor<T> constructor() {
         return constructor;
     }
 
     public Map<String, Integer> ordinals() {
         return ordinals;
     }
 
 }