package org.openmhealth.shim.common.mapper;


import org.openmhealth.schema.domain.omh.DataPoint;
import org.openmhealth.schema.domain.omh.SchemaId;
import org.openmhealth.schema.domain.omh.SchemaSupport;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.List;


/**
 * A mapper that creates data points from one or more inputs.
 *
 * @param <B> the body type of the data points to create
 * @param <I> the input type
 * @author Emerson Farrugia
 */
public interface DataPointMapper<B extends SchemaSupport, I> {

    /**
     * Maps one or more inputs into one or more data points. The parameter cardinality allows a mapper to use different
     * inputs to assemble a data point, e.g. combining a user profile API response and a blood pressure API response to
     * build an identified blood pressure data point.
     *
     * @param inputs the list of inputs
     * @return the list of data points mapped from those inputs
     */
    List<DataPoint<B>> asDataPoints(List<I> inputs);

    /**
     * Gets the schema identifier of the data point body that this mapper creates. This default implementation assumes
     * that body classes have a default constructor used for serialization, and must be overridden if they don't.
     *
     * @return the schema identifier of the body type
     */
    @SuppressWarnings("unchecked")
    default SchemaId getBodySchemaId() {

        try {
            Class<B> bodyClass = (Class<B>)
                    ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

            Constructor<B> bodyClassConstructor = bodyClass.getDeclaredConstructor();
            bodyClassConstructor.setAccessible(true);

            return bodyClassConstructor.newInstance().getSchemaId();
        }
        catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

package org.openmhealth.shim.common.mapper;


import org.openmhealth.schema.domain.omh.DataPoint;
import org.openmhealth.schema.domain.omh.SchemaId;
import org.openmhealth.schema.domain.omh.SchemaSupport;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import static java.util.Collections.singletonList;


/**
 * A mapper that creates data points from one or more inputs.
 *
 * @param <B> the body type of the data points to create
 * @param <I> the input type
 * @author Emerson Farrugia
 */
public interface DataPointMapper<B extends SchemaSupport, I> {

    /**
     * Maps one or more inputs into one or more data points. The parameter cardinality allows a mapper to use different
     * inputs to assemble a data point, e.g. combining a user profile API response and a blood pressure API response to
     * build an identified blood pressure data point.
     *
     * @param inputs the list of inputs
     * @return the list of data points mapped from those inputs
     */
    List<DataPoint<B>> asDataPoints(List<I> inputs);

    /**
     * @see #asDataPoints(List)
     */
    default List<DataPoint<B>> asDataPoints(I input) {

        return asDataPoints(singletonList(input));
    }

    /**
     * Gets the schema identifier of the data point body that this mapper creates. This default implementation assumes
     * that body classes have a default constructor used for serialization, and must be overridden if they don't.
     *
     * @return the schema identifier of the body type
     */
    @SuppressWarnings("unchecked")
    default SchemaId getBodySchemaId() {

        try {
            Class<B> bodyClass = (Class<B>)
                    ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

            Constructor<B> bodyClassConstructor = bodyClass.getDeclaredConstructor();
            bodyClassConstructor.setAccessible(true);

            return bodyClassConstructor.newInstance().getSchemaId();
        }
        catch (NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

Add a single response convenience method to data point mappers