class MeasurementMapper {

    @Nonnull
    <M> Point toPoint(@Nonnull final M measurement, @Nonnull final WritePrecision precision) throws InfluxException {

        Arguments.checkNotNull(measurement, "measurement");

        Class<?> measurementType = measurement.getClass();
        cacheMeasurementClass(measurementType);

        if (measurementType.getAnnotation(Measurement.class) == null) {
            String message = String
                    .format("Measurement type '%s' does not have a @Measurement annotation.", measurementType);

            throw new InfluxException(message);
        }

        Point point = Point.measurement(getMeasurementName(measurementType));

        CLASS_FIELD_CACHE.get(measurementType.getName()).forEach((name, field) -> {

            Column column = field.getAnnotation(Column.class);

            Object value;
            try {
                field.setAccessible(true);
                value = field.get(measurement);
            } catch (IllegalAccessException e) {

                throw new InfluxException(e);
            }

            if (value == null) {
                Object[] params = {field.getName(), measurement};
                LOG.log(Level.FINEST, "Field {0} of {1} has null value", params);
                return;
            }

            Class<?> fieldType = field.getType();
            if (column.tag()) {
                point.addTag(name, value.toString());
            } else if (column.timestamp()) {
                Instant instant = (Instant) value;
                point.time(instant, precision);
            } else if (isNumber(fieldType)) {
                point.addField(name, (Number) value);
            } else if (Boolean.class.isAssignableFrom(fieldType) || boolean.class.isAssignableFrom(fieldType)) {
                point.addField(name, (Boolean) value);
            } else if (String.class.isAssignableFrom(fieldType)) {
                point.addField(name, (String) value);
            } else {
                point.addField(name, value.toString());
            }
        });

        LOG.log(Level.FINEST, "Mapped measurement: {0} to Point: {1}", new Object[]{measurement, point});

        return point;
    }

}