All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mosip.pms.common.util.MapperUtils Maven / Gradle / Ivy

There is a newer version: 1.3.0-dp.1
Show newest version

package io.mosip.pms.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

import org.springframework.stereotype.Component;

import io.mosip.kernel.core.dataaccess.exception.DataAccessLayerException;
import io.mosip.kernel.core.util.EmptyCheckUtils;
import io.mosip.pms.common.dto.SearchAuthPolicy;
import io.mosip.pms.common.entity.AuthPolicy;
import io.mosip.pms.common.entity.BaseEntity;


/**
 * MapperUtils class provides methods to map or copy values from source object
 * to destination object.
 * 
 * @author Bal Vikash Sharma
 * @author Urvil Joshi
 * @since 1.0.0
 * @see MapperUtils
 *
 */
@Component
@SuppressWarnings("unchecked")
public class MapperUtils {

	/*
	 * @Autowired private ObjectMapper mapper;
	 */

	private MapperUtils() {
		super();
	}

	private static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
	private static final String SOURCE_NULL_MESSAGE = "source should not be null";
	private static final String DESTINATION_NULL_MESSAGE = "destination should not be null";

	/**
	 * This flag is used to restrict copy null values.
	 */
	private static Boolean mapNullValues = Boolean.TRUE;

	/**
	 * Parse a date string of pattern UTC_DATETIME_PATTERN into
	 * {@link LocalDateTime}
	 * 
	 * @param dateTime of type {@link String} of pattern UTC_DATETIME_PATTERN
	 * @return a {@link LocalDateTime} of given pattern
	 */
	public static LocalDateTime parseToLocalDateTime(String dateTime) {
		return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN));
	}

	/*
	 * #############Public method used for mapping################################
	 */

	/**
	 * This method map the values from source to
	 * destination if name and type of the fields inside the given
	 * parameters are same.If any of the parameters are null this
	 * method return null.This method internally check whether the
	 * source or destinationClass is DTO or an Entity type and map accordingly. If
	 * any {@link Collection} type or Entity type field is their then only matched
	 * name fields value will be set but not the embedded IDs and super class
	 * values.
	 * 
	 * @param            is a type parameter
	 * @param            is a type parameter
	 * @param source        which value is going to be mapped
	 * @param destination   where values is going to be mapped
	 * @param mapNullValues by default marked as true so, it will map null values
	 *                      but if marked as false then null values will be ignored
	 * @return the destination object
	 * @throws NullPointerException if either source or
	 *                              destination is null
	 */
	public static  D map(final S source, D destination, Boolean mapNullValues) {
		MapperUtils.mapNullValues = mapNullValues;
		return map(source, destination);
	}

	/**
	 * This method map the values from source to
	 * destination if name and type of the fields inside the given
	 * parameters are same.If any of the parameters are null this
	 * method return null.This method internally check whether the
	 * source or destinationClass is DTO or an Entity type and map accordingly. If
	 * any {@link Collection} type or Entity type field is their then only matched
	 * name fields value will be set but not the embedded IDs and super class
	 * values.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @return the destination object
	 * @throws NullPointerException if either source or
	 *                              destination is null
	 */
	public static  D map(final S source, D destination) {
		Objects.requireNonNull(source, SOURCE_NULL_MESSAGE);
		Objects.requireNonNull(destination, DESTINATION_NULL_MESSAGE);
		try {
			mapValues(source, destination);
		} catch (IllegalAccessException | InstantiationException e) {
			throw new DataAccessLayerException("KER-MSD-991", "Exception in mapping vlaues from source : "
					+ source.getClass().getName() + " to destination : " + destination.getClass().getName(), e);
		}
		return destination;
	}

	/**
	 * This method takes source and destinationClass, take
	 * all values from source and create an object of destinationClass
	 * and map all the values from source to destination if field name and type is
	 * same.This method internally check whether the source or destinationClass is
	 * DTO or an Entity type and map accordingly.If any {@link Collection} type or
	 * Entity type field is their then only matched name fields value will be set
	 * but not the embedded IDs and super class values.
	 * 
	 * @param               is a type parameter
	 * @param               is a type parameter
	 * @param source           which value is going to be mapped
	 * @param destinationClass where values is going to be mapped
	 * @return the object of destinationClass
	 * @throws DataAccessLayerException if exception occur during creating of
	 *                                  destinationClass object
	 * @throws NullPointerException     if either source or
	 *                                  destinationClass is null
	 */
	public static  D map(final S source, Class destinationClass) {
		Objects.requireNonNull(source, SOURCE_NULL_MESSAGE);
		Objects.requireNonNull(destinationClass, "destination class should not be null");
		Object destination = null;
		try {
			destination = destinationClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new DataAccessLayerException("KER-MSD-991", "Exception in mapping vlaues from source : "
					+ source.getClass().getName() + " to destination : " + destinationClass.getClass().getName(), e);
		}
		return (D) map(source, destination);
	}

	/**
	 * This method takes sourceList and destinationClass,
	 * take all values from source and create an object of
	 * destinationClass and map all the values from source to
	 * destination if field name and type is same.
	 * 
	 * @param               is a type parameter
	 * 
	 * @param               is a type parameter
	 * @param sourceList       which value is going to be mapped
	 * @param destinationClass where values is going to be mapped
	 * @return list of destinationClass objects
	 * @throws DataAccessLayerException if exception occur during creating of
	 *                                  destinationClass object
	 * @throws NullPointerException     if either sourceList or
	 *                                  destinationClass is null
	 */
	public static  List mapAll(final Collection sourceList, Class destinationClass) {
		Objects.requireNonNull(sourceList, "sourceList should not be null");
		Objects.requireNonNull(destinationClass, "destinationClass should not be null");
		return sourceList.stream().map(entity -> map(entity, destinationClass)).collect(Collectors.toList());
	}

	/**
	 * This method map values of source object to
	 * destination object. It will map field values having same name
	 * and same type for the fields. It will not map any field which is static or
	 * final.It will simply ignore those values.
	 * 
	 * @param          is a type parameter
	 * 
	 * @param          is a type parameter
	 * @param source      is any object which should not be null and have data which
	 *                    is going to be copied
	 * @param destination is an object in which source field values is going to be
	 *                    matched
	 * 
	 * @throws DataAccessLayerException if error raised during mapping values
	 * @throws NullPointerException     if either source or
	 *                                  destination is null
	 */
	public static  void mapFieldValues(S source, D destination) {

		Objects.requireNonNull(source, SOURCE_NULL_MESSAGE);
		Objects.requireNonNull(destination, DESTINATION_NULL_MESSAGE);
		Field[] sourceFields = source.getClass().getDeclaredFields();
		Field[] destinationFields = destination.getClass().getDeclaredFields();

		mapFieldValues(source, destination, sourceFields, destinationFields);

	}

	/**
	 * Map values from {@link BaseEntity} class source object to destination or vice
	 * versa and this method will be used to map {@link BaseEntity} values from
	 * entity to entity. Like when both source and
	 * destination are object which extends {@link BaseEntity}.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 */
	public static  void setBaseFieldValue(S source, D destination) {
		Objects.requireNonNull(source, SOURCE_NULL_MESSAGE);
		Objects.requireNonNull(destination, DESTINATION_NULL_MESSAGE);
		String sourceSupername = source.getClass().getSuperclass().getName();// super class of source object
		String destinationSupername = destination.getClass().getSuperclass().getName();// super class of destination
		// object
		String baseEntityClassName = source.getClass().getName();// base entity fully qualified name
		String objectClassName = Object.class.getName();// object class fully qualified name

		String baseDtoClassName = source.getClass().getName();// base entity fully qualified name

		if (sourceSupername.equals(baseEntityClassName) && destinationSupername.equals(baseDtoClassName)) {
			Field[] sourceFields = source.getClass().getSuperclass().getDeclaredFields();
			Field[] destinationFields = destination.getClass().getSuperclass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
			sourceFields = source.getClass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
			return;
		}
		if (sourceSupername.equals(baseDtoClassName) && destinationSupername.equals(baseEntityClassName)) {
			Field[] sourceFields = source.getClass().getSuperclass().getDeclaredFields();
			Field[] destinationFields = destination.getClass().getSuperclass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
			destinationFields = destination.getClass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
			return;
		}

		// if source is an entity
		if (sourceSupername.equals(baseEntityClassName) && !destinationSupername.equals(baseEntityClassName)) {
			Field[] sourceFields = source.getClass().getSuperclass().getDeclaredFields();
			Field[] destinationFields = destination.getClass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
		} else if (destinationSupername.equals(baseEntityClassName) && !sourceSupername.equals(baseEntityClassName)) {
			// if destination is an entity
			Field[] sourceFields = source.getClass().getDeclaredFields();
			Field[] destinationFields = destination.getClass().getSuperclass().getDeclaredFields();
			mapFieldValues(source, destination, sourceFields, destinationFields);
		} else {
			if (!sourceSupername.equals(objectClassName) && !destinationSupername.equals(objectClassName)) {
				Field[] sourceFields = source.getClass().getSuperclass().getDeclaredFields();
				Field[] destinationFields = destination.getClass().getSuperclass().getDeclaredFields();
				mapFieldValues(source, destination, sourceFields, destinationFields);
			}
		}

	}

	/*
	 * #############Private method used for mapping################################
	 */

	/**
	 * Map values from source object to destination object.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @throws InstantiationException if not able to create instance of field having
	 *                                annotation {@link EmbeddedId}
	 * @throws IllegalAccessException if provided fields are not accessible
	 */
	private static  void mapValues(S source, D destination)
			throws IllegalAccessException, InstantiationException {

		mapFieldValues(source, destination);// this method simply map values if field name and type are same

		if (source.getClass().isAnnotationPresent(Entity.class)) {
			mapEntityToDto(source, destination);
		} else {
			mapDtoToEntity(source, destination);
		}
	}

	/**
	 * This method map source DTO to a class object which extends {@link BaseEntity}
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @throws InstantiationException if not able to create instance of field having
	 *                                annotation {@link EmbeddedId}
	 * @throws IllegalAccessException if provided fields are not accessible
	 */
	private static  void mapDtoToEntity(S source, D destination)
			throws InstantiationException, IllegalAccessException {
		Field[] fields = destination.getClass().getDeclaredFields();
		setBaseFieldValue(source, destination);// map super class values
		for (Field field : fields) {
			/**
			 * Map DTO matching field values to super class field values
			 */
			if (field.isAnnotationPresent(EmbeddedId.class)) {
				Object id = field.getType().newInstance();
				mapFieldValues(source, id);
				field.setAccessible(true);
				field.set(destination, id);
				field.setAccessible(false);
				break;
			}
		}
	}

	/**
	 * Map source which extends {@link BaseEntity} to a DTO object.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @throws IllegalAccessException if provided fields are not accessible
	 */
	private static  void mapEntityToDto(S source, D destination) throws IllegalAccessException {
		Field[] sourceFields = source.getClass().getDeclaredFields();
		/*
		 * Here source is a Entity so we need to take values from Entity object and set
		 * the matching fields in the destination object mostly an DTO.
		 */
		boolean isIdMapped = false;// a flag to check if there any composite key is present and is mapped
		boolean isSuperMapped = false;// a flag to check is class extends the BaseEntity and is mapped
		for (Field sfield : sourceFields) {
			sfield.setAccessible(true);// mark accessible true because fields my be private, for safety
			if (!isIdMapped && sfield.isAnnotationPresent(EmbeddedId.class)) {
				/**
				 * Map the composite key values from source to destination if field name is same
				 */
				/**
				 * Take the field and get the composite key object and map all values to
				 * destination object
				 */
				mapFieldValues(sfield.get(source), destination);
				sfield.setAccessible(false);
				isIdMapped = true;// set flag so no need to check and map again
			} else if (!isSuperMapped) {
				setBaseFieldValue(source, destination);// this method check whether source is entity or destination
														// and maps values accordingly
				isSuperMapped = true;
			}
		}
	}

	/**
	 * Map values from source field to destination.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @param sf          source fields
	 * @param dtf         destination fields
	 */
	private static  void mapFieldValues(S source, D destination, Field[] sourceFields,
			Field[] destinationFields) {
		try {
			for (Field sfield : sourceFields) {
				// Do not set values either static or final
				if (Modifier.isStatic(sfield.getModifiers()) || Modifier.isFinal(sfield.getModifiers())) {
					continue;
				}

				// make field accessible possibly private
				sfield.setAccessible(true);

				for (Field dfield : destinationFields) {

					Class sourceType = sfield.getType();
					Class destinationType = dfield.getType();

					// map only those field whose name and type is same
					if (sfield.getName().equals(dfield.getName()) && sourceType.equals(destinationType)) {

						// for normal field values
						dfield.setAccessible(true);
						setFieldValue(source, destination, sfield, dfield);
						break;
					}
				}
			}
		} catch (IllegalAccessException e) {

			throw new DataAccessLayerException("KER-MSD-993", "Exception raised while mapping values form "
					+ source.getClass().getName() + " to " + destination.getClass().getName(), e);
		}
	}

	/**
	 * Take value from source field and insert value into destination field.
	 * 
	 * @param          is a type parameter
	 * @param          is a type parameter
	 * @param source      which value is going to be mapped
	 * @param destination where values is going to be mapped
	 * @param sf          source fields
	 * @param dtf         destination fields
	 * @throws IllegalAccessException if provided fields are not accessible
	 */
	private static  void setFieldValue(S source, D destination, Field sf, Field dtf)
			throws IllegalAccessException {
		// check whether user wants to map null values into destination object or not
		if (!mapNullValues && EmptyCheckUtils.isNullEmpty(sf.get(source))) {
			return;
		}
		dtf.set(destination, sf.get(source));
		dtf.setAccessible(false);
		sf.setAccessible(false);
	}
	
	
	public static List mapAuthPolicySearch(List authPolicies){
		Objects.requireNonNull(authPolicies);
		List authPoliciesList=new ArrayList<>();
		authPolicies.forEach(authPolicy -> {
			SearchAuthPolicy searchAuthPolicy=new SearchAuthPolicy();
			searchAuthPolicy.setCrBy(authPolicy.getCrBy());
			searchAuthPolicy.setCrDtimes(authPolicy.getCrDtimes());
			searchAuthPolicy.setDelDtimes(authPolicy.getDelDtimes());
			searchAuthPolicy.setDesc(authPolicy.getDescr());
			searchAuthPolicy.setId(authPolicy.getId());
			searchAuthPolicy.setIsActive(authPolicy.getIsActive());
			searchAuthPolicy.setIsDeleted(authPolicy.getIsDeleted());
			searchAuthPolicy.setName(authPolicy.getName());
			searchAuthPolicy.setPolicies(authPolicy.getPolicyFileId());
			searchAuthPolicy.setPolicyGroupId(authPolicy.getPolicyGroup().getId());
			searchAuthPolicy.setPolicyGroupName(authPolicy.getPolicyGroup().getName());
			searchAuthPolicy.setPolicyType(authPolicy.getPolicy_type());
			searchAuthPolicy.setSchema(authPolicy.getPolicySchema());
			searchAuthPolicy.setUpdBy(authPolicy.getUpdBy());
			searchAuthPolicy.setUpdDtimes(authPolicy.getUpdDtimes());
			searchAuthPolicy.setValidFromDate(authPolicy.getValidFromDate());
			searchAuthPolicy.setValidToDate(authPolicy.getValidToDate());
			searchAuthPolicy.setVersion(authPolicy.getVersion());
			authPoliciesList.add(searchAuthPolicy);
		});
		return authPoliciesList;
	}

	




}