// Path: RosettaCore/src/main/java/com/hubspot/rosetta/RosettaMapper.java
// public class RosettaMapper<T> {
//   private static TableNameExtractor TABLE_NAME_EXTRACTOR = chooseTableNameExtractor();
// 
//   private final JavaType type;
//   private final ObjectMapper objectMapper;
//   private final String tableName;
// 
//   public RosettaMapper(Class<T> type) {
//     this(type, Rosetta.getMapper());
//   }
// 
//   public RosettaMapper(Class<T> type, ObjectMapper objectMapper) {
//     this(type, objectMapper, null);
//   }
// 
//   public RosettaMapper(Class<T> type, String tableName) {
//     this(type, Rosetta.getMapper(), tableName);
//   }
// 
//   public RosettaMapper(Class<T> type, ObjectMapper objectMapper, String tableName) {
//     this((Type) type, objectMapper, tableName);
//   }
// 
//   public RosettaMapper(Type type, ObjectMapper objectMapper, String tableName) {
//     this.type = objectMapper.constructType(type);
//     this.objectMapper = objectMapper;
//     this.tableName = tableName;
//   }
// 
//   /**
//    * Map a single ResultSet row to a T instance.
//    *
//    * @throws SQLException
//    */
//   public T mapRow(ResultSet rs) throws SQLException {
//     Map<String, Object> map = new HashMap<String, Object>();
//     ResultSetMetaData metadata = rs.getMetaData();
// 
//     for (int i = 1; i <= metadata.getColumnCount(); ++i) {
//       String label = metadata.getColumnLabel(i);
// 
//       final Object value;
//       // calling getObject on a BLOB/CLOB produces weird results
//       switch (metadata.getColumnType(i)) {
//         case Types.BLOB:
//           value = rs.getBytes(i);
//           break;
//         case Types.CLOB:
//           value = rs.getString(i);
//           break;
//         default:
//           value = rs.getObject(i);
//       }
// 
//       // don't use table name extractor because we don't want aliased table name
//       boolean overwrite = this.tableName != null && this.tableName.equals(metadata.getTableName(i));
//       String tableName = TABLE_NAME_EXTRACTOR.getTableName(metadata, i);
//       if (tableName != null && !tableName.isEmpty()) {
//         String qualifiedName = tableName + "." + metadata.getColumnName(i);
//         add(map, qualifiedName, value, overwrite);
//       }
// 
//       add(map, label, value, overwrite);
//     }
// 
//     return objectMapper.convertValue(map, type);
//   }
// 
//   private void add(Map<String, Object> map, String label, Object value, boolean overwrite) {
//     if (label.contains(".")) {
//       int periodIndex = label.indexOf('.');
//       String prefix = label.substring(0, periodIndex);
//       String suffix = label.substring(periodIndex + 1);
// 
//       @SuppressWarnings("unchecked")
//       Map<String, Object> submap = (Map<String, Object>) map.get(prefix);
//       if (submap == null) {
//         submap = new HashMap<>();
//         map.put(prefix, submap);
//       }
// 
//       add(submap, suffix, value, overwrite);
//     } else {
//       if (overwrite || !map.containsKey(label)) {
//         map.put(label, value);
//       }
//     }
//   }
// 
//   private static TableNameExtractor chooseTableNameExtractor() {
//     try {
//       Class.forName("com.mysql.jdbc.ResultSetMetaData");
//       return (TableNameExtractor) Class.forName("com.mysql.jdbc.MysqlTableNameExtractor").newInstance();
//     } catch (Exception e) {
//       return new TableNameExtractor.Default();
//     }
//   }
// }
// 
// Path: RosettaCore/src/main/java/com/hubspot/rosetta/util/SqlTableNameExtractor.java
// public class SqlTableNameExtractor {
// 
//   private SqlTableNameExtractor() {
//     throw new AssertionError();
//   }
// 
//   public static String extractTableName(final String sql) {
//     String lowerCaseSql = sql.toLowerCase();
// 
//     String from = " from ";
//     int fromIndex = lowerCaseSql.indexOf(from);
//     if (fromIndex < 0) {
//       return null;
//     }
// 
//     String tableString = sql.substring(fromIndex + from.length());
//     if (tableString.startsWith("(")) {
//       return null;
//     }
// 
//     int endTableIndex = -1;
//     for (int i = 0; i < tableString.length(); i++) {
//       char c = tableString.charAt(i);
//       if (c == ' ' || c == ',' || c == ';') {
//         endTableIndex = i;
//         break;
//       }
//     }
// 
//     return endTableIndex < 0 ? tableString : tableString.substring(0, endTableIndex);
//   }
// }


import java.lang.reflect.Type;
import java.util.Optional;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.generic.GenericTypes;
import org.jdbi.v3.core.mapper.ColumnMappers;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.mapper.RowMapperFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hubspot.rosetta.RosettaMapper;
import com.hubspot.rosetta.util.SqlTableNameExtractor;
package com.hubspot.rosetta.jdbi3;




public class RosettaRowMapperFactory implements RowMapperFactory {

  @Override
  public Optional<RowMapper<?>> build(Type type, ConfigRegistry config) {
    if (accepts(type, config)) {
      return Optional.of((rs, ctx) -> {
        ObjectMapper objectMapper = ctx.getConfig(RosettaObjectMapper.class).getObjectMapper();
        String tableName = SqlTableNameExtractor.extractTableName(ctx.getParsedSql().getSql());
        final RosettaMapper mapper = new RosettaMapper(type, objectMapper, tableName);

        return mapper.mapRow(rs);
      });
    } else {
      return Optional.empty();
    }
  }

  private static boolean accepts(Type type, ConfigRegistry config) {
    Class<?> rawType = GenericTypes.getErasedType(type);

// Path: RosettaCore/src/main/java/com/hubspot/rosetta/RosettaMapper.java
// public class RosettaMapper<T> {
//   private static TableNameExtractor TABLE_NAME_EXTRACTOR = chooseTableNameExtractor();
// 
//   private final JavaType type;
//   private final ObjectMapper objectMapper;
//   private final String tableName;
// 
//   public RosettaMapper(Class<T> type) {
//     this(type, Rosetta.getMapper());
//   }
// 
//   public RosettaMapper(Class<T> type, ObjectMapper objectMapper) {
//     this(type, objectMapper, null);
//   }
// 
//   public RosettaMapper(Class<T> type, String tableName) {
//     this(type, Rosetta.getMapper(), tableName);
//   }
// 
//   public RosettaMapper(Class<T> type, ObjectMapper objectMapper, String tableName) {
//     this((Type) type, objectMapper, tableName);
//   }
// 
//   public RosettaMapper(Type type, ObjectMapper objectMapper, String tableName) {
//     this.type = objectMapper.constructType(type);
//     this.objectMapper = objectMapper;
//     this.tableName = tableName;
//   }
// 
//   /**
//    * Map a single ResultSet row to a T instance.
//    *
//    * @throws SQLException
//    */
//   public T mapRow(ResultSet rs) throws SQLException {
//     Map<String, Object> map = new HashMap<String, Object>();
//     ResultSetMetaData metadata = rs.getMetaData();
// 
//     for (int i = 1; i <= metadata.getColumnCount(); ++i) {
//       String label = metadata.getColumnLabel(i);
// 
//       final Object value;
//       // calling getObject on a BLOB/CLOB produces weird results
//       switch (metadata.getColumnType(i)) {
//         case Types.BLOB:
//           value = rs.getBytes(i);
//           break;
//         case Types.CLOB:
//           value = rs.getString(i);
//           break;
//         default:
//           value = rs.getObject(i);
//       }
// 
//       // don't use table name extractor because we don't want aliased table name
//       boolean overwrite = this.tableName != null && this.tableName.equals(metadata.getTableName(i));
//       String tableName = TABLE_NAME_EXTRACTOR.getTableName(metadata, i);
//       if (tableName != null && !tableName.isEmpty()) {
//         String qualifiedName = tableName + "." + metadata.getColumnName(i);
//         add(map, qualifiedName, value, overwrite);
//       }
// 
//       add(map, label, value, overwrite);
//     }
// 
//     return objectMapper.convertValue(map, type);
//   }
// 
//   private void add(Map<String, Object> map, String label, Object value, boolean overwrite) {
//     if (label.contains(".")) {
//       int periodIndex = label.indexOf('.');
//       String prefix = label.substring(0, periodIndex);
//       String suffix = label.substring(periodIndex + 1);
// 
//       @SuppressWarnings("unchecked")
//       Map<String, Object> submap = (Map<String, Object>) map.get(prefix);
//       if (submap == null) {
//         submap = new HashMap<>();
//         map.put(prefix, submap);
//       }
// 
//       add(submap, suffix, value, overwrite);
//     } else {
//       if (overwrite || !map.containsKey(label)) {
//         map.put(label, value);
//       }
//     }
//   }
// 
//   private static TableNameExtractor chooseTableNameExtractor() {
//     try {
//       Class.forName("com.mysql.jdbc.ResultSetMetaData");
//       return (TableNameExtractor) Class.forName("com.mysql.jdbc.MysqlTableNameExtractor").newInstance();
//     } catch (Exception e) {
//       return new TableNameExtractor.Default();
//     }
//   }
// }
// 
// Path: RosettaCore/src/main/java/com/hubspot/rosetta/util/SqlTableNameExtractor.java
// public class SqlTableNameExtractor {
// 
//   private SqlTableNameExtractor() {
//     throw new AssertionError();
//   }
// 
//   public static String extractTableName(final String sql) {
//     String lowerCaseSql = sql.toLowerCase();
// 
//     String from = " from ";
//     int fromIndex = lowerCaseSql.indexOf(from);
//     if (fromIndex < 0) {
//       return null;
//     }
// 
//     String tableString = sql.substring(fromIndex + from.length());
//     if (tableString.startsWith("(")) {
//       return null;
//     }
// 
//     int endTableIndex = -1;
//     for (int i = 0; i < tableString.length(); i++) {
//       char c = tableString.charAt(i);
//       if (c == ' ' || c == ',' || c == ';') {
//         endTableIndex = i;
//         break;
//       }
//     }
// 
//     return endTableIndex < 0 ? tableString : tableString.substring(0, endTableIndex);
//   }
// }


// Path: RosettaJdbi3/src/main/java/com/hubspot/rosetta/jdbi3/RosettaRowMapperFactory.java
import java.lang.reflect.Type;
import java.util.Optional;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.generic.GenericTypes;
import org.jdbi.v3.core.mapper.ColumnMappers;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.mapper.RowMapperFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hubspot.rosetta.RosettaMapper;
import com.hubspot.rosetta.util.SqlTableNameExtractor;

package com.hubspot.rosetta.jdbi3;




public class RosettaRowMapperFactory implements RowMapperFactory {

  @Override
  public Optional<RowMapper<?>> build(Type type, ConfigRegistry config) {
    if (accepts(type, config)) {
      return Optional.of((rs, ctx) -> {
        ObjectMapper objectMapper = ctx.getConfig(RosettaObjectMapper.class).getObjectMapper();
        String tableName = SqlTableNameExtractor.extractTableName(ctx.getParsedSql().getSql());
        final RosettaMapper mapper = new RosettaMapper(type, objectMapper, tableName);

        return mapper.mapRow(rs);
      });
    } else {
      return Optional.empty();
    }
  }

  private static boolean accepts(Type type, ConfigRegistry config) {
    Class<?> rawType = GenericTypes.getErasedType(type);

    if (rawType.isPrimitive() || rawType.isArray() || rawType.isAnnotation()) {