@SuppressWarnings("unchecked")
  @Override
  public <P> Getter<GettableByIndexData, P> newGetter(
      Type target, DatastaxColumnKey key, ColumnDefinition<?, ?> columnDefinition) {
    Class<?> targetClass = TypeHelper.toClass(target);
    if (Date.class.equals(targetClass)) {
      return (Getter<GettableByIndexData, P>) new DatastaxDateGetter(key.getIndex());
    }

    if (boolean.class.equals(targetClass) || Boolean.class.equals(targetClass)) {
      return (Getter<GettableByIndexData, P>) new DatastaxBooleanGetter(key.getIndex());
    }

    if (InetAddress.class.equals(targetClass)) {
      return (Getter<GettableByIndexData, P>) new DatastaxInetAddressGetter(key.getIndex());
    }

    if (TupleValue.class.equals(targetClass)) {
      return (Getter<GettableByIndexData, P>) new DatastaxTupleValueGetter(key.getIndex());
    }

    if (Collection.class.isAssignableFrom(targetClass)) {

      Type elementType = TypeHelper.getComponentTypeOfListOrArray(target);
      Class<?> dataTypeClass = Object.class;
      Class<?> dataTypeElt = null;
      DataType dtElt = null;
      if (key.getDataType() != null) {
        DataType dataType = key.getDataType();
        dataTypeClass = dataType.asJavaClass();
        if (dataType.isCollection()) {
          dtElt = key.getDataType().getTypeArguments().get(0);
          dataTypeElt = dtElt.asJavaClass();
        }
      } else {
        dataTypeElt = TypeHelper.toClass(elementType);
      }

      if (dataTypeElt != null) {
        if (TypeHelper.areEquals(elementType, dataTypeElt)) {
          if (Set.class.equals(dataTypeClass)) {
            if (targetClass.isAssignableFrom(dataTypeClass)) {
              return new DatastaxSetGetter(key.getIndex(), TypeHelper.toClass(elementType));
            }
          }
          if (List.class.equals(dataTypeClass)) {
            if (targetClass.isAssignableFrom(dataTypeClass)) {
              return new DatastaxListGetter(key.getIndex(), TypeHelper.toClass(elementType));
            }
          }
        } else {
          Converter<?, ?> converter = getConverter(elementType, dataTypeElt, dtElt);

          if (converter != null) {
            if (Set.class.equals(dataTypeClass)) {
              if (targetClass.isAssignableFrom(dataTypeClass)) {
                return new DatastaxSetWithConverterGetter(key.getIndex(), dataTypeElt, converter);
              }
            }
            if (List.class.equals(dataTypeClass)) {
              if (targetClass.isAssignableFrom(dataTypeClass)) {
                return new DatastaxListWithConverterGetter(key.getIndex(), dataTypeElt, converter);
              }
            }
          }
        }
      }
    }
    if (Map.class.equals(targetClass)) {
      Tuple2<Type, Type> keyValueTypeOfMap = TypeHelper.getKeyValueTypeOfMap(target);

      Class<?> dtKeyType = null;
      Class<?> dtValueType = null;
      DataType dtKey = null;
      DataType dtValue = null;
      if (key.getDataType() != null) {
        List<DataType> typeArguments = key.getDataType().getTypeArguments();
        if (typeArguments.size() == 2) {
          dtKey = typeArguments.get(0);
          dtKeyType = dtKey.asJavaClass();
          dtValue = typeArguments.get(1);
          dtValueType = dtValue.asJavaClass();
        }
      } else {
        dtKeyType = TypeHelper.toClass(keyValueTypeOfMap.first());
        dtValueType = TypeHelper.toClass(keyValueTypeOfMap.second());
      }
      if (dtKeyType != null && dtValueType != null) {
        if (TypeHelper.areEquals(keyValueTypeOfMap.first(), dtKeyType)
            && TypeHelper.areEquals(keyValueTypeOfMap.second(), dtValueType)) {
          return new DatastaxMapGetter(
              key.getIndex(),
              TypeHelper.toClass(keyValueTypeOfMap.first()),
              TypeHelper.toClass(keyValueTypeOfMap.second()));
        } else {
          Converter<?, ?> keyConverter = getConverter(keyValueTypeOfMap.first(), dtKeyType, dtKey);
          Converter<?, ?> valueConverter =
              getConverter(keyValueTypeOfMap.second(), dtValueType, dtValue);

          if (keyConverter != null && valueConverter != null) {
            return new DatastaxMapWithConverterGetter(
                key.getIndex(), dtKeyType, dtValueType, keyConverter, valueConverter);
          }
        }
      }
    }

    if (Tuples.isTuple(target)) {
      if (key.getDataType() != null && key.getDataType() instanceof TupleType) {
        TupleType tt = (TupleType) key.getDataType();

        List<DataType> typeArguments = tt.getTypeArguments();

        TypeVariable<? extends Class<?>>[] typeParameters = targetClass.getTypeParameters();

        if (typeArguments.size() <= typeParameters.length) {
          return (Getter<GettableByIndexData, P>)
              DatastaxTupleGetter.newInstance(datastaxMapperFactory, target, tt, key.getIndex());
        }
      }
    }

    if (TypeHelper.isEnum(target)) {
      final Getter<GettableByIndexData, ? extends Enum> getter =
          enumGetter(key, TypeHelper.toClass(target));
      if (getter != null) {
        return (Getter<GettableByIndexData, P>) getter;
      }
    }

    final GetterFactory<GettableByIndexData, DatastaxColumnKey> rowGetterFactory =
        getterFactories.get(targetClass);

    if (rowGetterFactory != null) {
      return rowGetterFactory.newGetter(target, key, columnDefinition);
    }

    final Getter<GettableByIndexData, P> getter =
        jodaTimeGetterFactory.newGetter(target, key, columnDefinition);

    if (getter != null) {
      return getter;
    }

    if (key.getDataType() != null && key.getDataType() instanceof UserType) {
      UserType ut = (UserType) key.getDataType();
      return (Getter<GettableByIndexData, P>)
          DatastaxUDTGetter.newInstance(datastaxMapperFactory, target, ut, key.getIndex());
    }

    return null;
  }
  public static List<InstantiatorDefinition> extractDefinitions(final Type target)
      throws IOException {
    final List<InstantiatorDefinition> constructors = new ArrayList<InstantiatorDefinition>();

    final Class<?> targetClass = TypeHelper.toClass(target);

    ClassLoader cl = targetClass.getClassLoader();
    if (cl == null) {
      cl = ClassLoader.getSystemClassLoader();
    }

    final String fileName = targetClass.getName().replace('.', '/') + ".class";
    final InputStream is = cl.getResourceAsStream(fileName);
    try {
      if (is == null) {
        throw new IOException("Cannot find file " + fileName + " in " + cl);
      }
      ClassReader classReader = new ClassReader(is);
      classReader.accept(
          new ClassVisitor(Opcodes.ASM5) {
            List<String> genericTypeNames;

            @Override
            public void visit(
                int version,
                int access,
                String name,
                String signature,
                String superName,
                String[] interfaces) {
              if (signature != null) {
                genericTypeNames = AsmUtils.extractGenericTypeNames(signature);
              } else {
                genericTypeNames = Collections.emptyList();
              }
              super.visit(version, access, name, signature, superName, interfaces);
            }

            @Override
            public MethodVisitor visitMethod(
                int access,
                final String methodName,
                String desc,
                String signature,
                String[] exceptions) {
              final boolean isConstructor = "<init>".equals(methodName);
              if ((Opcodes.ACC_PUBLIC & access) == Opcodes.ACC_PUBLIC
                  && (isConstructor
                      || ((Opcodes.ACC_STATIC & access) == Opcodes.ACC_STATIC
                          && !desc.endsWith("V")))) {
                final List<String> descTypes = AsmUtils.extractTypeNames(desc);

                final List<String> genericTypes;
                final List<String> names = new ArrayList<String>();
                if (signature != null) {
                  genericTypes = AsmUtils.extractTypeNames(signature);
                } else {
                  genericTypes = descTypes;
                }

                if (!isConstructor) {
                  if (descTypes.size() > 0) {
                    try {
                      final Type genericType =
                          AsmUtils.toGenericType(
                              descTypes.get(descTypes.size() - 1), genericTypeNames, target);
                      if (!targetClass.isAssignableFrom(TypeHelper.toClass(genericType))) {
                        return null;
                      }
                    } catch (ClassNotFoundException e) {
                      return null;
                    }
                  } else return null;
                }

                return new MethodVisitor(Opcodes.ASM5) {

                  Label firstLabel;
                  Label lastLabel;

                  @Override
                  public void visitLabel(Label label) {
                    if (firstLabel == null) {
                      firstLabel = label;
                    }
                    lastLabel = label;
                  }

                  @Override
                  public void visitLocalVariable(
                      String name,
                      String desc,
                      String signature,
                      Label start,
                      Label end,
                      int index) {
                    if (start.equals(firstLabel) && end.equals(lastLabel) && !"this".equals(name)) {
                      names.add(name);
                    }
                  }

                  @Override
                  public void visitEnd() {
                    try {
                      final List<Parameter> parameters = new ArrayList<Parameter>();

                      int l = descTypes.size() - (isConstructor ? 0 : 1);
                      for (int i = 0; i < l; i++) {
                        String name = "arg" + i;
                        if (i < names.size()) {
                          name = names.get(i);
                        }
                        parameters.add(
                            createParameter(i, name, descTypes.get(i), genericTypes.get(i)));
                      }

                      final Member executable;

                      if (isConstructor) {
                        executable = targetClass.getDeclaredConstructor(toTypeArray(parameters));
                      } else {
                        executable =
                            targetClass.getDeclaredMethod(methodName, toTypeArray(parameters));
                      }
                      constructors.add(
                          new ExecutableInstantiatorDefinition(
                              executable, parameters.toArray(new Parameter[0])));
                    } catch (Exception e) {
                      ErrorHelper.rethrow(e);
                    }
                  }

                  private Class<?>[] toTypeArray(List<Parameter> parameters) {
                    Class<?>[] types = new Class<?>[parameters.size()];
                    for (int i = 0; i < types.length; i++) {
                      types[i] = parameters.get(i).getType();
                    }
                    return types;
                  }

                  private Parameter createParameter(
                      int index, String name, String desc, String signature) {
                    try {

                      Type basicType = AsmUtils.toGenericType(desc, genericTypeNames, target);
                      Type genericType = basicType;
                      if (signature != null) {
                        Type type = AsmUtils.toGenericType(signature, genericTypeNames, target);
                        if (type != null) {
                          genericType = type;
                        }
                      }
                      return new Parameter(index, name, TypeHelper.toClass(basicType), genericType);
                    } catch (ClassNotFoundException e) {
                      throw new Error("Unexpected error " + e, e);
                    }
                  }
                };
              } else {
                return null;
              }
            }
          },
          0);
    } finally {
      try {
        is.close();
      } catch (Exception e) {
      }
    }

    return constructors;
  }