Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / common / mapping / DbImportSynonymMapper.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.io.common.mapping;
10
11 import java.sql.ResultSet;
12 import java.sql.SQLException;
13
14 import org.apache.commons.lang3.StringUtils;
15 import org.apache.logging.log4j.LogManager;
16 import org.apache.logging.log4j.Logger;
17
18 import eu.etaxonomy.cdm.io.common.DbImportStateBase;
19 import eu.etaxonomy.cdm.io.common.ICdmIO;
20 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
21 import eu.etaxonomy.cdm.model.common.Annotation;
22 import eu.etaxonomy.cdm.model.common.CdmBase;
23 import eu.etaxonomy.cdm.model.common.IRelationshipType;
24 import eu.etaxonomy.cdm.model.common.Language;
25 import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
26 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
27 import eu.etaxonomy.cdm.model.reference.Reference;
28 import eu.etaxonomy.cdm.model.taxon.Synonym;
29 import eu.etaxonomy.cdm.model.taxon.SynonymType;
30 import eu.etaxonomy.cdm.model.taxon.Taxon;
31 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
32 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
33
34 /**
35 * A mapper that can be used to create synonym relationships for real synonyms with a {@link SynonymType},
36 * synonyms relationships that are handled as concept relationships if the synonym needs to be handled
37 * as accepted taxon (misapplied names, pro parte synonyms, synonyms with attached factual data, ...)
38 * and even synonym relationships which in reality are only name relationships.
39 * The mapping is defined in the transformer.
40 *
41 * @author a.mueller
42 * @since 02.03.2010
43 *
44 * @see DbImportTaxIncludedInMapper
45 */
46 public class DbImportSynonymMapper<STATE extends DbImportStateBase<?,?>>
47 extends DbImportMultiAttributeMapperBase<CdmBase, STATE> {
48
49 private static final Logger logger = LogManager.getLogger();
50
51 //******************************* ATTRIBUTES ***************************************/
52
53 private String fromAttribute;
54 private String toAttribute;
55 private TaxonRelationshipType taxonRelType;
56 private SynonymType synType;
57 private String relatedObjectNamespace;
58 private String citationAttribute;
59 private String microCitationAttribute;
60 private String relationshipTypeAttribute;
61 boolean forceTaxonLevelRelation = true;
62 boolean saveSourceValueAsAnnotation;
63
64 //******************************** FACTORY METHOD ***************************************************/
65
66 /**
67 * Creates a new instance of SynonymMapper.
68 * @param dbFromAttribute
69 * @param dbToAttribute
70 * @param relatedObjectNamespace
71 * @param relTypeAttribute the attribute in the source DB to compute the relationship type(s) from
72 * @param defaultTaxonRelationshipType this relationship type is taken for accepted taxa
73 * being synonyms (may be the case if data are dirty)
74 * @return
75 */
76 public static DbImportSynonymMapper<?> NewInstance(String dbFromAttribute, String dbToAttribute,
77 String relatedObjectTaxonNamespace, String relTypeAttribute, SynonymType defaultSynonymType,
78 TaxonRelationshipType defaultTaxonRelationshipType, boolean saveSourceValueAsAnnotation){
79 return new DbImportSynonymMapper<>(dbFromAttribute, dbToAttribute, defaultSynonymType, defaultTaxonRelationshipType, relatedObjectTaxonNamespace, relTypeAttribute, saveSourceValueAsAnnotation);
80 }
81
82 //********************************* CONSTRUCTOR ****************************************/
83
84 protected DbImportSynonymMapper(String fromAttribute, String toAttribute, SynonymType synType,
85 TaxonRelationshipType relType, String relatedObjectNamespace, String relTypeAttribute
86 , boolean saveSourceValueAsAnnotation) {
87 //TODO make it a single attribute mapper (?)
88 this.fromAttribute = fromAttribute;
89 this.toAttribute = toAttribute;
90 this.taxonRelType = relType;
91 this.synType = synType;
92 this.relatedObjectNamespace = relatedObjectNamespace;
93 this.relationshipTypeAttribute = relTypeAttribute;
94 this.saveSourceValueAsAnnotation = saveSourceValueAsAnnotation;
95 }
96
97 //************************************ METHODS *******************************************/
98
99 @Override
100 public CdmBase invoke(ResultSet rs, CdmBase cdmBase) throws SQLException {
101 STATE state = getState();
102 ICdmIO<?> currentImport = state.getCurrentIO();
103 if (currentImport instanceof ICheckIgnoreMapper){
104 boolean ignoreRecord = ((ICheckIgnoreMapper)currentImport).checkIgnoreMapper(this, rs);
105 if (ignoreRecord){
106 return cdmBase;
107 }
108 }
109
110 TaxonBase<?> fromObject = (TaxonBase<?>)getRelatedObject(rs, fromAttribute);
111 TaxonBase<?> toObject = (TaxonBase<?>)getRelatedObject(rs, toAttribute);
112 String fromId = rs.getObject(fromAttribute)== null ? null: String.valueOf(rs.getObject(fromAttribute));
113 String toId = rs.getObject(toAttribute) == null? null : String.valueOf(rs.getObject(toAttribute));
114
115 if (toId == null){
116 return fromObject;
117 }
118
119 Reference citation = CdmBase.deproxy(getRelatedObject(rs, citationAttribute), Reference.class);
120 String microCitation = null;
121 if (citationAttribute != null){
122 microCitation = rs.getString(microCitationAttribute);
123 }
124 SynonymType synType = this.synType;
125 NameRelationshipType nameType = null;
126 HybridRelationshipType hybridType = null;
127 TaxonRelationshipType taxonRelType = this.taxonRelType;
128
129 String relTypeAttrValue = null;
130 if (relationshipTypeAttribute != null){
131 relTypeAttrValue = rs.getString(relationshipTypeAttribute);
132 IRelationshipType[] relTypes = this.getState().getTransformer().getSynonymRelationTypesByKey(relTypeAttrValue, state);
133 if (relTypes[0]!= null){
134 synType = (SynonymType)relTypes[0];
135 }
136 if (relTypes[1]!=null){
137 taxonRelType = (TaxonRelationshipType)relTypes[1];
138 }
139 nameType = (NameRelationshipType)relTypes[2];
140 hybridType = (HybridRelationshipType)relTypes[3];
141 }
142
143 if (fromObject == null){
144 String warning = "The synonym (" + fromId + ") could not be found. Synonym not added to accepted taxon";
145 logger.warn(warning);
146 return cdmBase;
147 }
148 checkSynonymType(fromObject, fromId, taxonRelType);
149
150 if (toObject == null){
151 String warning = "The accepted taxon (" + toId + ") could not be found. Synonym not added to accepted taxon";
152 logger.warn(warning);
153 return cdmBase;
154 }
155 Taxon taxon = checkTaxonType(toObject, "Accepted taxon", toId);
156
157 if(forceTaxonLevelRelation && synType == null){
158 synType = SynonymType.SYNONYM_OF;
159 }
160
161 AnnotatableEntity[] result = new AnnotatableEntity[4];
162 if (fromObject.isInstanceOf(Synonym.class) && synType != null){
163 Synonym synonym = CdmBase.deproxy(fromObject, Synonym.class);
164 taxon.addSynonym(synonym, synType); //citation and micro citation not in use anymore as we do not have synonym relationships anymore
165 result[0] = synonym;
166 }else if (fromObject.isInstanceOf(Taxon.class) && taxonRelType != null){
167 TaxonRelationshipType type = taxonRelType;
168 Taxon synonymTaxon = CdmBase.deproxy(fromObject, Taxon.class);
169 result[1] = synonymTaxon.addTaxonRelation(taxon, type, citation, microCitation);
170 }else{
171 if (forceTaxonLevelRelation){
172 String message = "Taxon is not a synonym and accepted taxa are not allowed as synonyms: " + fromObject.getTitleCache() + "; " + fromObject.getId();
173 logger.warn(message);
174 }
175 }
176 if(nameType != null){
177 result[2] = fromObject.getName().addRelationshipToName(toObject.getName(), nameType, citation, microCitation, null, null);
178 }
179 if(hybridType != null){
180 result[3] = fromObject.getName().addHybridParent(toObject.getName(), hybridType, citation, microCitation, null, null);
181 }
182 if (saveSourceValueAsAnnotation && StringUtils.isNotBlank(relTypeAttrValue)){
183 for (AnnotatableEntity a : result){
184 if (a != null){
185 a.addAnnotation(Annotation.NewInstance(relTypeAttrValue, Language.UNKNOWN_LANGUAGE()));
186 }
187 }
188 }
189
190 return fromObject;
191 }
192
193 /**
194 * //TODO copied from DbImportObjectMapper. Maybe these can be merged again in future
195 */
196 protected CdmBase getRelatedObject(ResultSet rs, String dbAttribute) throws SQLException {
197 CdmBase result = null;
198 if (dbAttribute != null){
199 Object dbValue = rs.getObject(dbAttribute);
200 String id = String.valueOf(dbValue);
201 DbImportStateBase<?,?> state = importMapperHelper.getState();
202 result = state.getRelatedObject(relatedObjectNamespace, id);
203 }
204 return result;
205 }
206
207 /**
208 * Checks if cdmBase is of type Taxon
209 */
210 private Taxon checkTaxonType(TaxonBase<?> taxonBase, String typeString, String id) {
211 if (! taxonBase.isInstanceOf(Taxon.class)){
212 String warning = typeString + " (" + id + ") is not of type Taxon but of type " + taxonBase.getClass().getSimpleName();
213 logger.warn(warning);
214 throw new IllegalArgumentException(warning);
215 }
216 return (CdmBase.deproxy(taxonBase, Taxon.class));
217 }
218
219 /**
220 * Checks if cdmBase is of type Synonym
221 */
222 private TaxonBase<?> checkSynonymType(CdmBase cdmBase, String id, TaxonRelationshipType taxonRelType) {
223 if (! cdmBase.isInstanceOf(Synonym.class)){
224 String warning = "Synonym (" + id + ") is not of type Synonym but of type " + cdmBase.getClass().getSimpleName();
225 if (taxonRelType == null){
226 logger.warn(warning);
227 throw new IllegalArgumentException(warning);
228 }else{
229 logger.info(warning);
230 }
231 }
232 return (CdmBase.deproxy(cdmBase, TaxonBase.class));
233 }
234 }