MapstructMapper.java

/*
 * Copyright 2023 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gringlobal.api.v2.mapper;

import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.model.ClassPK;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.metadata.ImageMetadata;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.filerepository.model.RepositoryFolder;
import org.genesys.filerepository.model.RepositoryImage;
import org.gringlobal.api.model.AccessionActionDTO;
import org.gringlobal.api.model.AccessionActionRequestDTO;
import org.gringlobal.api.model.AccessionDTO;
import org.gringlobal.api.model.AccessionInfo;
import org.gringlobal.api.model.AccessionInvAnnotationDTO;
import org.gringlobal.api.model.AccessionInvAttachDTO;
import org.gringlobal.api.model.AccessionInvGroupDTO;
import org.gringlobal.api.model.AccessionInvGroupInfo;
import org.gringlobal.api.model.AccessionInvNameDTO;
import org.gringlobal.api.model.AccessionInvNameInfo;
import org.gringlobal.api.model.AccessionIprDTO;
import org.gringlobal.api.model.AccessionIprInfo;
import org.gringlobal.api.model.AccessionPedigreeDTO;
import org.gringlobal.api.model.AccessionPedigreeInfo;
import org.gringlobal.api.model.AccessionQuarantineDTO;
import org.gringlobal.api.model.AccessionSourceDTO;
import org.gringlobal.api.model.AccessionSourceInfo;
import org.gringlobal.api.model.AccessionSourceMapDTO;
import org.gringlobal.api.model.AclSidInfo;
import org.gringlobal.api.model.AcquisitionDTO;
import org.gringlobal.api.model.AuditLogDTO;
import org.gringlobal.api.model.CitationDTO;
import org.gringlobal.api.model.CooperatorInfo;
import org.gringlobal.api.model.FeedbackDTO;
import org.gringlobal.api.model.FeedbackInfo;
import org.gringlobal.api.model.GeneticMarkerInfo;
import org.gringlobal.api.model.GeographyDTO;
import org.gringlobal.api.model.GeographyInfo;
import org.gringlobal.api.model.InventoryActionDTO;
import org.gringlobal.api.model.InventoryActionRequestDTO;
import org.gringlobal.api.model.InventoryDTO;
import org.gringlobal.api.model.InventoryExtraDTO;
import org.gringlobal.api.model.InventoryInfo;
import org.gringlobal.api.model.InventoryMaintenancePolicyInfo;
import org.gringlobal.api.model.InventoryViabilityActionDTO;
import org.gringlobal.api.model.InventoryViabilityActionRequestDTO;
import org.gringlobal.api.model.InventoryViabilityDTO;
import org.gringlobal.api.model.InventoryViabilityDataDTO;
import org.gringlobal.api.model.InventoryViabilityDetailsDTO;
import org.gringlobal.api.model.InventoryViabilityInfo;
import org.gringlobal.api.model.InventoryViabilityRuleInfo;
import org.gringlobal.api.model.LiteratureInfo;
import org.gringlobal.api.model.MaterielInfo;
import org.gringlobal.api.model.MethodInfo;
import org.gringlobal.api.model.NameGroupInfo;
import org.gringlobal.api.model.NotificationScheduleDTO;
import org.gringlobal.api.model.OrderRequestActionDTO;
import org.gringlobal.api.model.OrderRequestActionRequestDTO;
import org.gringlobal.api.model.OrderRequestAttachDTO;
import org.gringlobal.api.model.OrderRequestDTO;
import org.gringlobal.api.model.OrderRequestDetailsDTO;
import org.gringlobal.api.model.OrderRequestInfo;
import org.gringlobal.api.model.OrderRequestItemActionDTO;
import org.gringlobal.api.model.OrderRequestItemActionRequestDTO;
import org.gringlobal.api.model.OrderRequestItemDTO;
import org.gringlobal.api.model.OrderRequestItemInfo;
import org.gringlobal.api.model.RepositoryFileDTO;
import org.gringlobal.api.model.RepositoryFileInfo;
import org.gringlobal.api.model.RepositoryFolderInfo;
import org.gringlobal.api.model.RepositoryImageDTO;
import org.gringlobal.api.model.SiteInfo;
import org.gringlobal.api.model.SourceDescObservationDTO;
import org.gringlobal.api.model.SourceDescriptorCodeDTO;
import org.gringlobal.api.model.SourceDescriptorCodeInfo;
import org.gringlobal.api.model.SourceDescriptorCodeLangDTO;
import org.gringlobal.api.model.SourceDescriptorDTO;
import org.gringlobal.api.model.SourceDescriptorInfo;
import org.gringlobal.api.model.SourceDescriptorLangDTO;
import org.gringlobal.api.model.SysLangDTO;
import org.gringlobal.api.model.SysLangInfo;
import org.gringlobal.api.model.TaxonomyFamilyInfo;
import org.gringlobal.api.model.TaxonomyGenusInfo;
import org.gringlobal.api.model.TaxonomySpeciesInfo;
import org.gringlobal.api.model.TranslatedSourceDescObservationDTO;
import org.gringlobal.api.model.TranslatedSourceDescriptorCodeDTO;
import org.gringlobal.api.model.TranslatedSourceDescriptorCodeInfo;
import org.gringlobal.api.model.TranslatedSourceDescriptorDTO;
import org.gringlobal.api.model.TranslatedSourceDescriptorInfo;
import org.gringlobal.api.model.WebOrderRequestInfo;
import org.gringlobal.api.v1.MultiOp;
import org.gringlobal.model.Accession;
import org.gringlobal.model.AccessionAction;
import org.gringlobal.model.AccessionInvAnnotation;
import org.gringlobal.model.AccessionInvAttach;
import org.gringlobal.model.AccessionInvGroup;
import org.gringlobal.model.AccessionInvName;
import org.gringlobal.model.AccessionIpr;
import org.gringlobal.model.AccessionPedigree;
import org.gringlobal.model.AccessionQuarantine;
import org.gringlobal.model.AccessionSource;
import org.gringlobal.model.AccessionSourceMap;
import org.gringlobal.model.Citation;
import org.gringlobal.model.Cooperator;
import org.gringlobal.model.Feedback;
import org.gringlobal.model.GeneticMarker;
import org.gringlobal.model.Geography;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.InventoryAction;
import org.gringlobal.model.InventoryExtra;
import org.gringlobal.model.InventoryMaintenancePolicy;
import org.gringlobal.model.InventoryViability;
import org.gringlobal.model.InventoryViabilityAction;
import org.gringlobal.model.InventoryViabilityData;
import org.gringlobal.model.InventoryViabilityRule;
import org.gringlobal.model.Literature;
import org.gringlobal.model.Materiel;
import org.gringlobal.model.Method;
import org.gringlobal.model.NameGroup;
import org.gringlobal.model.OrderRequest;
import org.gringlobal.model.OrderRequestAction;
import org.gringlobal.model.OrderRequestAttach;
import org.gringlobal.model.OrderRequestItem;
import org.gringlobal.model.OrderRequestItemAction;
import org.gringlobal.model.SeedInventoryExtra;
import org.gringlobal.model.Site;
import org.gringlobal.model.SourceDescObservation;
import org.gringlobal.model.SourceDescriptor;
import org.gringlobal.model.SourceDescriptorCode;
import org.gringlobal.model.SourceDescriptorCodeLang;
import org.gringlobal.model.SourceDescriptorLang;
import org.gringlobal.model.SysLang;
import org.gringlobal.model.TaxonomyFamily;
import org.gringlobal.model.TaxonomyGenus;
import org.gringlobal.model.TaxonomySpecies;
import org.gringlobal.model.TissueCultureExtra;
import org.gringlobal.model.WebOrderRequest;
import org.gringlobal.model.schedule.NotificationSchedule;
import org.gringlobal.service.AccessionActionService;
import org.gringlobal.service.AccessionService;
import org.gringlobal.service.OrderRequestService;
import org.gringlobal.service.InventoryActionService.InventoryActionRequest;
import org.gringlobal.service.InventoryViabilityActionService.InventoryViabilityActionRequest;
import org.gringlobal.service.InventoryViabilityService.InventoryViabilityDetails;
import org.gringlobal.service.OrderRequestActionService.OrderRequestActionRequest;
import org.gringlobal.service.OrderRequestItemActionService.OrderRequestItemActionRequest;
import org.gringlobal.service.SourceDescObservationService;
import org.gringlobal.service.SourceDescriptorCodeTranslationService;
import org.gringlobal.service.SourceDescriptorTranslationService;
import org.mapstruct.BeanMapping;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.SubclassMapping;
import org.springframework.data.domain.Page;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.FIELD,
	imports = { Objects.class, SecurityContextUtil.class, ImageMetadata.class }
)
public interface MapstructMapper {

	String Y = "Y"; // GG true
	String N = "N"; // GG false

	default <A, B> List<B> map(@NotNull List<A> source, @NotNull Function<A, B> mapper) {
		var res = new ArrayList<B>(source.size());
		source.forEach(x -> res.add(mapper.apply(x)));
		return res;
	}

	default <A, B> Set<B> map(@NotNull Set<A> source, @NotNull Function<A, B> mapper) {
		var res = new HashSet<B>(source.size());
		source.forEach(x -> res.add(mapper.apply(x)));
		return res;
	}

	default <A, B> Page<B> map(@NotNull Page<A> source, @NotNull Function<A, B> mapper) {
		return source.map(mapper);
	}

	default <A, B> MultiOp<B> map(@NotNull MultiOp<A> source, @NotNull Function<A, B> mapper) {
		return new MultiOp<>(this.map(source.success, mapper), source.errors);
	}

	@Named("mapStringYN")
	default boolean mapStringYN(String ynValue) {
		return Objects.equals("Y", ynValue);
	}

	@Named("mapBooleanYN")
	default String mapBooleanYN(boolean value) {
		return value ? Y : N;
	}

	default Long map(AclSid value) {
		return value == null ? null : value.getId();
	}

	default AclSid map(Long id) {
		AclSid sid = null;
		if (id != null) {
			sid = new AclSid();
			sid.setId(id);
		}
		return sid;
	}

	@Mapping(source = "isBackedUp", target = "backedUp", qualifiedByName = "mapStringYN")
	@Mapping(source = "isCore", target = "core", qualifiedByName = "mapStringYN")
	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionDTO map(Accession accession);

	@Mapping(source = "backedUp", target = "isBackedUp", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "core", target = "isCore", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "accessionSources", ignore = true)
	@Mapping(target = "inventories", ignore = true)
	@Mapping(target = "accessionActions", ignore = true)
	@Mapping(target = "accessionIprs", ignore = true)
	@Mapping(target = "accessionPedigree", ignore = true)
	@Mapping(target = "accessionQuarantines", ignore = true)
	@Mapping(target = "citations", ignore = true)
	@Mapping(target = "names", ignore = true)
	Accession map(AccessionDTO accession);

	SiteInfo mapInfo(Site site);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Site mapInfo(SiteInfo site);

	@Mapping(target = "genusName", expression = "java(ts.getTaxonomyGenus() == null ? null : ts.getTaxonomyGenus().getGenusName())")
	@Mapping(source = "isSpecificHybrid", target = "specificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(source = "isSubspecificHybrid", target = "subspecificHybrid", qualifiedByName = "mapStringYN")
	@Mapping(target = "current", expression = "java(ts.getCurrentTaxonomySpecies() != null && Objects.equals(ts.getId(), ts.getCurrentTaxonomySpecies().getId()))")
	TaxonomySpeciesInfo mapInfo(TaxonomySpecies ts);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "speciesName", target = "speciesName")
	@Mapping(source = "name", target = "name")
	@Mapping(source = "genusName", target = "taxonomyGenus", qualifiedByName = "taxonomyGenusFromGenusName")
	TaxonomySpecies mapInfo(TaxonomySpeciesInfo info);

	@Named("taxonomyGenusFromGenusName")
	default TaxonomyGenus taxonomyGenusFromGenusName(String genusName) {
		TaxonomyGenus genus = null;
		if (genusName != null) {
			genus = new TaxonomyGenus();
			genus.setGenusName(genusName);
		}
		return genus;
	}

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionAction map(AccessionActionDTO dto);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionActionDTO map(AccessionAction accessionAction);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "sid", target = "sid")
	AclSid mapInfo (AclSidInfo aclSidInfo);

	AclSidInfo mapInfo (AclSid aclSid);

	@Mapping(target = "current", expression = "java(c.getCurrentCooperator() == null || Objects.equals(c.getId(), c.getCurrentCooperator().getId()))")
	CooperatorInfo mapInfo(Cooperator c);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Cooperator mapInfo(CooperatorInfo info);

	AccessionInfo mapInfo(Accession a);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	@Mapping(target = "accessionNumber", source = "accessionNumber")
	@Mapping(target = "doi", source = "doi")
	@Mapping(target = "taxonomySpecies", source = "taxonomySpecies")
	@Mapping(target = "site", source = "site")
	Accession mapInfo(AccessionInfo info);

	MethodInfo mapInfo(Method method);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Method mapInfo(MethodInfo info);

	@Mapping(target = "webVisible", source = "webVisible", qualifiedByName = "mapBooleanYN")
	AccessionActionService.AccessionActionRequest map(AccessionActionRequestDTO dto);
	
	@Mapping(target = "webVisible", source = "webVisible", qualifiedByName = "mapStringYN")
	AccessionActionRequestDTO map(AccessionActionService.AccessionActionRequest request);

	AccessionInvAnnotationDTO map(AccessionInvAnnotation annotation);

	AccessionInvAnnotation map(AccessionInvAnnotationDTO dto);

	@Mapping(target = "accessionNumber", source="accession.accessionNumber")
	@Mapping(target = "available", source = "isAvailable", qualifiedByName = "mapStringYN")
	@Mapping(target = "distributable", source = "isDistributable", qualifiedByName = "mapStringYN")
	InventoryInfo mapInfo(Inventory inventory);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Inventory mapInfo(InventoryInfo info);

	@Mapping(target = "finalRecipient", source = "finalRecipientCooperator")
	OrderRequestInfo mapInfo(OrderRequest orderRequest);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	OrderRequest mapInfo(OrderRequestInfo info);

	OrderRequestItemDTO map(OrderRequestItem orderRequestItem);

	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "webOrderRequestItem", ignore = true)
	OrderRequestItem map(OrderRequestItemDTO dto);

	@Mapping(target = "inventoryNumber", source = "inventory.inventoryNumber")
	@Mapping(target = "accessionNumber", source = "inventory.accession.accessionNumber")
	OrderRequestItemInfo mapInfo(OrderRequestItem orderRequestItem);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	OrderRequestItem mapInfo(OrderRequestItemInfo orderRequestItem);

	OrderRequestActionDTO map(OrderRequestAction action);
	OrderRequestAction map(OrderRequestActionDTO action);
	OrderRequestActionRequest map(OrderRequestActionRequestDTO dto);
	OrderRequestActionRequestDTO map(OrderRequestActionRequest dto);

	OrderRequestItemActionDTO map(OrderRequestItemAction action);
	OrderRequestItemAction map(OrderRequestItemActionDTO action);
	OrderRequestItemActionRequest map(OrderRequestItemActionRequestDTO dto);
	OrderRequestItemActionRequestDTO map(OrderRequestItemActionRequest dto);

	OrderRequestDetailsDTO map(OrderRequestService.OrderRequestDetails details);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	OrderRequestAttachDTO map(OrderRequestAttach attach);

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	OrderRequestAttach map(OrderRequestAttachDTO dto);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionInvAttachDTO map(AccessionInvAttach attach);

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionInvAttach map(AccessionInvAttachDTO dto);

	@SubclassMapping(target = RepositoryImageDTO.class, source = RepositoryImage.class)
	RepositoryFileDTO map(RepositoryFile repositoryFile);

	@SubclassMapping(target = RepositoryImage.class, source = RepositoryImageDTO.class)
	@Mapping(target = "apply", ignore = true)
	RepositoryFile map(RepositoryFileDTO dto);

	@Mapping(target = "public", expression = "java(SecurityContextUtil.anyoneHasPermission(repositoryImage, \"READ\"))")
	RepositoryImageDTO map(RepositoryImage repositoryImage);

	@Mapping(target = "orientation", expression = "java(ImageMetadata.Orientation.valueOf(dto.getOrientation()))")
	@Mapping(target = "apply", ignore = true)
	RepositoryImage map(RepositoryImageDTO dto);

	RepositoryFolderInfo mapInfo(RepositoryFolder repositoryFolder);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	RepositoryFolder mapInfo(RepositoryFolderInfo info) throws InvalidRepositoryPathException;

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "members", ignore = true)
	AccessionInvGroup map(AccessionInvGroupDTO dto);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionInvGroupDTO map(AccessionInvGroup group);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	AccessionInvGroup mapInfo(AccessionInvGroupInfo info);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionInvGroupInfo mapInfo(AccessionInvGroup group);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionInvNameDTO map(AccessionInvName invName);

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	AccessionInvName map(AccessionInvNameDTO dto);

	NameGroupInfo mapInfo(NameGroup nameGroup);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	NameGroup mapInfo(NameGroupInfo info);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	AccessionInvNameInfo mapInfo(AccessionInvName invName);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	AccessionInvName mapInfo(AccessionInvNameInfo info);

	AccessionIpr map(AccessionIprDTO dto);

	AccessionIprDTO map(AccessionIpr accessionIpr);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	AccessionIpr mapInfo(AccessionIprInfo info);

	AccessionIprInfo mapInfo(AccessionIpr accessionIpr);

	AccessionPedigree map(AccessionPedigreeDTO dto);

	AccessionPedigreeDTO map(AccessionPedigree accessionPedigree);

	AccessionPedigreeInfo mapInfo(AccessionPedigree accessionPedigree);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	AccessionPedigree mapInfo(AccessionPedigreeInfo info);

	AccessionQuarantineDTO map(AccessionQuarantine accessionQuarantine);

	AccessionQuarantine map(AccessionQuarantineDTO dto);

	@Mapping(source = "isWebVisible", target = "webVisible", qualifiedByName = "mapStringYN")
	@Mapping(source = "isOrigin", target = "origin", qualifiedByName = "mapStringYN")
	AccessionSourceDTO map(AccessionSource accessionSource);

	@Mapping(source = "webVisible", target = "isWebVisible", qualifiedByName = "mapBooleanYN")
	@Mapping(source = "origin", target = "isOrigin", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "cooperators", ignore = true)
	@Mapping(target = "sourceDescObservations", ignore = true)
	AccessionSource map(AccessionSourceDTO dto);

	@Mapping(source = "isValid", target = "valid", qualifiedByName = "mapStringYN")
	@Mapping(target = "current", expression = "java(g.getCurrentGeography() == null || Objects.equals(g.getId(), g.getCurrentGeography().getId()))")
	GeographyInfo mapInfo(Geography g);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Geography mapInfo(GeographyInfo dto);

	@Mapping(source = "isOrigin", target = "origin", qualifiedByName = "mapStringYN")
	AccessionSourceInfo mapInfo(AccessionSource accessionSource);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "accession", target = "accession")
	AccessionSource mapInfo(AccessionSourceInfo dto);

	AccessionSourceMapDTO map(AccessionSourceMap accessionSourceMap);

	AccessionSourceMap map(AccessionSourceMapDTO dto);

	AcquisitionDTO mapInfo(AccessionService.AcquisitionData acquisitionData);

	AccessionService.AcquisitionData mapInfo(AcquisitionDTO info);

	@Mapping(source = "isAcceptedName", target = "acceptedName", qualifiedByName = "mapStringYN")
	CitationDTO map(Citation citation);

	@Mapping(source = "acceptedName", target = "isAcceptedName", qualifiedByName = "mapBooleanYN")
	Citation map(CitationDTO dto);

	GeneticMarkerInfo mapInfo(GeneticMarker geneticMarker);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	GeneticMarker mapInfo(GeneticMarkerInfo info);

	LiteratureInfo mapInfo(Literature literature);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Literature mapInfo(LiteratureInfo info);

	TaxonomyFamilyInfo mapInfo(TaxonomyFamily taxonomyFamily);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	TaxonomyFamily mapInfo(TaxonomyFamilyInfo info);

	TaxonomyGenusInfo mapInfo(TaxonomyGenus genus);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	TaxonomyGenus mapInfo(TaxonomyGenusInfo info);

	@Mapping(source = "isRestrictedByInventory", target = "restrictedByInventory", qualifiedByName = "mapStringYN")
	FeedbackDTO map(Feedback feedback);

	@Mapping(source = "restrictedByInventory", target = "isRestrictedByInventory", qualifiedByName = "mapBooleanYN")
	Feedback map(FeedbackDTO dto);

	@Mapping(source = "isRestrictedByInventory", target = "restrictedByInventory", qualifiedByName = "mapStringYN")
	FeedbackInfo mapInfo(Feedback feedback);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Feedback mapInfo(FeedbackInfo dto);

	@Mapping(target = "currentGeographyId", expression = "java(g.getCurrentGeography() == null ? null : g.getCurrentGeography().getId())")
	@Mapping(source = "isValid", target = "valid", qualifiedByName = "mapStringYN")
	GeographyDTO map(Geography g);

	@Mapping(source = "currentGeographyId", target = "currentGeography", qualifiedByName = "currentGeography")
	@Mapping(source = "valid", target = "isValid", qualifiedByName = "mapBooleanYN")
	Geography map(GeographyDTO dto);

	@Named("currentGeography")
	default Geography currentGeography(Long currentGeographyId) {
		return currentGeographyId == null ? null : new Geography(currentGeographyId);
	}

	@Mapping(target = "autoDeducted", source = "isAutoDeducted", qualifiedByName = "mapStringYN")
	@Mapping(target = "available", source = "isAvailable", qualifiedByName = "mapStringYN")
	@Mapping(target = "distributable", source = "isDistributable", qualifiedByName = "mapStringYN")
	@Mapping(target = "parentInventoryId", expression = "java(i.getParentInventory() == null ? null : i.getParentInventory().getId())")
	InventoryDTO map(Inventory i);

	@Mapping(target = "isAutoDeducted", source = "autoDeducted", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isAvailable", source = "available", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "isDistributable", source = "distributable", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "parentInventory", source = "parentInventoryId", qualifiedByName = "parentInventory")
	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "quality", ignore = true)
	@Mapping(target = "viability", ignore = true)
	@Mapping(target = "annotations", ignore = true)
	@Mapping(target = "names", ignore = true)
	@Mapping(target = "accessionInvGroupMaps", ignore = true)
	Inventory map(InventoryDTO dto);

	InventoryAction map(InventoryActionDTO dto);
	InventoryActionDTO map(InventoryAction action);
	InventoryActionRequest map(InventoryActionRequestDTO dto);
	InventoryActionRequestDTO map(InventoryActionRequest request);

	@Named("parentInventory")
	default Inventory parentInventory(Long parentInventoryId) {
		return parentInventoryId == null ? null : new Inventory(parentInventoryId);
	}

	default InventoryExtraDTO map(InventoryExtra model) {
		if (model instanceof TissueCultureExtra)
			return map((TissueCultureExtra) model);
		else if (model instanceof SeedInventoryExtra) {
			return map((SeedInventoryExtra) model);
		} else if (model == null) {
			return null; // Handle null
		} else {
			throw new RuntimeException("Invalid extra type!");
		}
	}

	default InventoryExtra map(InventoryExtraDTO model) {
		if (model instanceof InventoryExtraDTO.TissueCultureExtraDTO)
			return map((InventoryExtraDTO.TissueCultureExtraDTO) model);
		else if (model instanceof InventoryExtraDTO.SeedInventoryExtraDTO) {
			return map((InventoryExtraDTO.SeedInventoryExtraDTO) model);
		} else {
			throw new RuntimeException("Invalid extra DTO type!");
		}
	}

	InventoryExtraDTO.TissueCultureExtraDTO map(TissueCultureExtra tissueCulture);

	TissueCultureExtra map(InventoryExtraDTO.TissueCultureExtraDTO dto);

	InventoryExtraDTO.SeedInventoryExtraDTO map(SeedInventoryExtra seedInventory);

	@Mapping(target = "lastViabilityResult", ignore = true)
	SeedInventoryExtra map(InventoryExtraDTO.SeedInventoryExtraDTO dto);

	@Mapping(source = "isAvailable", target = "available", qualifiedByName = "mapStringYN")
	MaterielInfo mapInfo(Materiel materiel);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	Materiel mapInfo(MaterielInfo info);

	@Mapping(source = "isAutoDeducted", target = "autoDeducted", qualifiedByName = "mapStringYN")
	InventoryMaintenancePolicyInfo mapInfo(InventoryMaintenancePolicy policy);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryMaintenancePolicy mapInfo(InventoryMaintenancePolicyInfo info);
	
	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryViabilityRule mapInfo(InventoryViabilityRuleInfo info);
	
	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "datas", ignore = true)
	InventoryViability map(InventoryViabilityDTO viability);

	InventoryViabilityDTO map(InventoryViability viability);

	InventoryViabilityDetailsDTO map(InventoryViabilityDetails details);

	InventoryViabilityInfo mapInfo(InventoryViability viability);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	InventoryViability mapInfo(InventoryViabilityInfo info);

	InventoryViabilityActionRequestDTO map(InventoryViabilityActionRequest dto);
	InventoryViabilityActionRequest map(InventoryViabilityActionRequestDTO dto);

	InventoryViabilityActionDTO map(InventoryViabilityAction action);
	InventoryViabilityAction map(InventoryViabilityActionDTO dto);

	@Mapping(target = "dataEnvironmentMaps", ignore = true)
	@Mapping(target = "orderRequestItem", ignore = true) // FIXME re-enable
	InventoryViabilityData map(InventoryViabilityDataDTO dto);

	InventoryViabilityDataDTO map(InventoryViabilityData viabilityData);

	OrderRequestDTO map(OrderRequest orderRequest);

	@Mapping(target = "actions", ignore = true)
	@Mapping(target = "attachments", ignore = true)
	@Mapping(target = "lastCompletedAction", ignore = true)
	@Mapping(target = "orderRequestItems", ignore = true)
	OrderRequest map(OrderRequestDTO dto);

	WebOrderRequestInfo mapInfo(WebOrderRequest webOrderRequest);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	WebOrderRequest mapInfo(WebOrderRequestInfo info);

	RepositoryFileInfo mapInfo(RepositoryFile repositoryFile);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	RepositoryFile mapInfo(RepositoryFileInfo info);

	@Mapping(target = "classPk", expression = "java(al.getClassPk() == null ? null : al.getClassPk().getClassname())")
	@Mapping(target = "referencedEntity", expression = "java(al.getReferencedEntity() == null ? null : al.getReferencedEntity().getClassname())")
	AuditLogDTO map(AuditLog al);

	@Mapping(source = "classPk", target = "classPk", qualifiedByName = "classPkFromName")
	@Mapping(source = "referencedEntity", target = "referencedEntity", qualifiedByName = "classPkFromName")
	AuditLog map(AuditLogDTO from);

	@Named("classPkFromName")
	default ClassPK classPkFromName(String classname) {
		ClassPK classPK = null;
		if (classname != null) {
			classPK = new ClassPK();
			classPK.setClassname(classname);
		}
		return classPK;
	}

	SourceDescObservationDTO map(SourceDescObservation sourceDescObservation);

	SourceDescObservation map(SourceDescObservationDTO dto);

	@Mapping(source = "isCoded", target = "coded", qualifiedByName = "mapStringYN")
	SourceDescriptorInfo mapInfo(SourceDescriptor sourceDescriptor);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	SourceDescriptor mapInfo(SourceDescriptorInfo sourceDescriptor);

	SourceDescriptorCodeInfo mapInfo(SourceDescriptorCode sourceDescriptorCode);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(source = "id", target = "id")
	@Mapping(source = "code", target = "code")
	SourceDescriptorCode mapInfo(SourceDescriptorCodeInfo info);

	SourceDescriptorCodeDTO map(SourceDescriptorCode sourceDescriptorCode);

	@Mapping(target = "langs", ignore = true)
	SourceDescriptorCode map(SourceDescriptorCodeDTO dto);
	
	@Mapping(source = "isCoded", target = "coded", qualifiedByName = "mapStringYN")
	SourceDescriptorDTO map(SourceDescriptor sourceDescriptor);
	
	@Mapping(source = "coded", target = "isCoded", qualifiedByName = "mapBooleanYN")
	@Mapping(target = "langs", ignore = true)
	SourceDescriptor map(SourceDescriptorDTO dto);

	@Mapping(source = "translatedSourceDescriptor", target = "sourceDescriptor")
	@Mapping(source = "translatedSourceDescriptorCode", target = "sourceDescriptorCode")
	@Mapping(source = "observation.numericValue", target = "numericValue")
	@Mapping(source = "observation.originalValue", target = "originalValue")
	@Mapping(source = "observation.stringValue", target = "stringValue")
	@Mapping(source = "observation.note", target = "note")
	@Mapping(source = "observation.accessionSource", target = "accessionSource")
	@Mapping(source = "observation.ownedBy", target = "ownedBy")
	@Mapping(source = "observation.ownedDate", target = "ownedDate")
	@Mapping(source = "observation.createdBy", target = "createdBy")
	@Mapping(source = "observation.createdDate", target = "createdDate")
	@Mapping(source = "observation.modifiedBy", target = "modifiedBy")
	@Mapping(source = "observation.modifiedDate", target = "modifiedDate")
	TranslatedSourceDescObservationDTO map(SourceDescObservationService.TranslatedSourceDescObservation tsdo);

	TranslatedSourceDescriptorCodeDTO map(SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode tsdc);

	@Mapping(target = "id", ignore = true)
	SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode map(TranslatedSourceDescriptorCodeDTO dto);

	TranslatedSourceDescriptorCodeInfo mapInfo(SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode tsdc);

	@Mapping(target = "id", ignore = true)
	SourceDescriptorCodeTranslationService.TranslatedSourceDescriptorCode mapInfo(TranslatedSourceDescriptorCodeInfo info);

	TranslatedSourceDescriptorDTO map(SourceDescriptorTranslationService.TranslatedSourceDescriptor tsd);

	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	SourceDescriptorTranslationService.TranslatedSourceDescriptor map(TranslatedSourceDescriptorDTO dto);
	
	TranslatedSourceDescriptorInfo mapInfo(SourceDescriptorTranslationService.TranslatedSourceDescriptor tsd);
	
	@Mapping(target = "id", ignore = true)
	@Mapping(target = "codes", ignore = true)
	SourceDescriptorTranslationService.TranslatedSourceDescriptor mapInfo(TranslatedSourceDescriptorInfo info);

	@Mapping(source = "isEnabled", target = "enabled", qualifiedByName = "mapStringYN")
	SysLangDTO map(SysLang sysLang);

	@Mapping(source = "enabled", target = "isEnabled", qualifiedByName = "mapBooleanYN")
	SysLang map(SysLangDTO dto);

	SourceDescriptorLangDTO map(SourceDescriptorLang lang);

	SourceDescriptorLang map(SourceDescriptorLangDTO dto);

	SourceDescriptorCodeLangDTO map(SourceDescriptorCodeLang lang);

	SourceDescriptorCodeLang map(SourceDescriptorCodeLangDTO dto);

	@BeanMapping(ignoreByDefault = true)
	@Mapping(target = "id", source = "id")
	SysLang mapInfo(SysLangInfo sysLang);

	@Mapping(source = "isEnabled", target = "enabled", qualifiedByName = "mapStringYN")
	SysLangInfo mapInfo(SysLang sysLang);

	NotificationSchedule map(NotificationScheduleDTO dto);
	
	NotificationScheduleDTO map(NotificationSchedule source);
}