ref #1444, ref #7976 better logging for unhandled unacceptReason
[cdmlib-apps.git] / cdm-pesi / src / main / java / eu / etaxonomy / cdm / io / pesi / erms / ErmsTaxonImport.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
10 package eu.etaxonomy.cdm.io.pesi.erms;
11
12 import java.sql.ResultSet;
13 import java.sql.SQLException;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.UUID;
21
22 import org.apache.commons.lang3.StringUtils;
23 import org.apache.log4j.Logger;
24 import org.hsqldb.lib.StringComparator;
25 import org.springframework.stereotype.Component;
26
27 import eu.etaxonomy.cdm.common.CdmUtils;
28 import eu.etaxonomy.cdm.io.common.DbImportStateBase;
29 import eu.etaxonomy.cdm.io.common.IOValidator;
30 import eu.etaxonomy.cdm.io.common.mapping.DbIgnoreMapper;
31 import eu.etaxonomy.cdm.io.common.mapping.DbImportAnnotationMapper;
32 import eu.etaxonomy.cdm.io.common.mapping.DbImportExtensionMapper;
33 import eu.etaxonomy.cdm.io.common.mapping.DbImportLsidMapper;
34 import eu.etaxonomy.cdm.io.common.mapping.DbImportMapping;
35 import eu.etaxonomy.cdm.io.common.mapping.DbImportMarkerMapper;
36 import eu.etaxonomy.cdm.io.common.mapping.DbImportMethodMapper;
37 import eu.etaxonomy.cdm.io.common.mapping.DbImportObjectCreationMapper;
38 import eu.etaxonomy.cdm.io.common.mapping.DbImportStringMapper;
39 import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
40 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
41 import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
42 import eu.etaxonomy.cdm.io.pesi.erms.validation.ErmsTaxonImportValidator;
43 import eu.etaxonomy.cdm.io.pesi.out.PesiTaxonExport;
44 import eu.etaxonomy.cdm.io.pesi.out.PesiTransformer;
45 import eu.etaxonomy.cdm.model.common.AnnotationType;
46 import eu.etaxonomy.cdm.model.common.CdmBase;
47 import eu.etaxonomy.cdm.model.common.ExtensionType;
48 import eu.etaxonomy.cdm.model.common.Language;
49 import eu.etaxonomy.cdm.model.common.MarkerType;
50 import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
51 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
52 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
53 import eu.etaxonomy.cdm.model.name.Rank;
54 import eu.etaxonomy.cdm.model.name.TaxonName;
55 import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
56 import eu.etaxonomy.cdm.model.reference.Reference;
57 import eu.etaxonomy.cdm.model.taxon.Synonym;
58 import eu.etaxonomy.cdm.model.taxon.Taxon;
59 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
60 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
61 import eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy;
62
63 /**
64 * @author a.mueller
65 * @since 20.02.2010
66 */
67 @Component
68 public class ErmsTaxonImport
69 extends ErmsImportBase<TaxonBase<?>>
70 implements IMappingImport<TaxonBase<?>, ErmsImportState>{
71
72 private static final long serialVersionUID = -7111568277264140051L;
73 private static final Logger logger = Logger.getLogger(ErmsTaxonImport.class);
74
75 private DbImportMapping<ErmsImportState, ErmsImportConfigurator> mapping;
76
77 private static final String pluralString = "taxa";
78 private static final String dbTableName = "tu";
79 private static final Class<?> cdmTargetClass = TaxonBase.class;
80
81 private static Map<String, Integer> unacceptReasons = new HashMap<>();
82
83 public ErmsTaxonImport(){
84 super(pluralString, dbTableName, cdmTargetClass);
85 }
86
87 @Override
88 protected String getIdQuery() {
89 String strQuery = " SELECT id FROM tu " ; //WHERE id NOT IN (147415) for now we exclude Monera as it has no children and is unclear what classification it has. In ERMS it is alternative accepted name (in https://en.wikipedia.org/wiki/Monera it might be a super taxon to bacteria).
90 return strQuery;
91 }
92
93 @Override
94 protected DbImportMapping<ErmsImportState, ErmsImportConfigurator> getMapping() {
95 if (mapping == null){
96 mapping = new DbImportMapping<>();
97
98 mapping.addMapper(DbImportObjectCreationMapper.NewInstance(this, "id", TAXON_NAMESPACE)); //id + tu_status
99 mapping.addMapper(DbImportLsidMapper.NewInstance("GUID", "lsid"));
100
101 UUID tsnUuid = ErmsTransformer.uuidExtTsn;
102 ExtensionType tsnExtType = getExtensionType(tsnUuid, "TSN", "TSN", "TSN");
103 mapping.addMapper(DbImportExtensionMapper.NewInstance("tsn", tsnExtType));
104 // mapping.addMapper(DbImportStringMapper.NewInstance("tu_name", "(NonViralName)name.nameCache"));
105
106 ExtensionType displayNameExtType = getExtensionType(ErmsTransformer.uuidExtDisplayName, "display name", "display name", "display name");
107 mapping.addMapper(DbImportExtensionMapper.NewInstance("tu_displayname", displayNameExtType));
108 //Ignore fuzzyName
109 // ExtensionType fuzzyNameExtType = getExtensionType(ErmsTransformer.uuidExtFuzzyName, "fuzzy name", "fuzzy name", "fuzzy name");
110 // mapping.addMapper(DbImportExtensionMapper.NewInstance("tu_fuzzyname", fuzzyNameExtType));
111 mapping.addMapper(DbImportStringMapper.NewInstance("tu_authority", "name.authorshipCache"));
112
113 ExtensionType fossilStatusExtType = getExtensionType(ErmsTransformer.uuidExtFossilStatus, "fossil status", "fossil status", "fos. stat.");
114 mapping.addMapper(DbImportExtensionMapper.NewInstance("fossil_name", fossilStatusExtType));
115
116 ExtensionType unacceptExtType = getExtensionType(ErmsTransformer.uuidExtUnacceptReason, "unaccept reason", "unaccept reason", "reason");
117 mapping.addMapper(DbImportExtensionMapper.NewInstance("tu_unacceptreason", unacceptExtType));
118
119 ExtensionType qualityStatusExtType = getExtensionType(ErmsTransformer.uuidExtQualityStatus, "quality status", "quality status", "quality status");
120 mapping.addMapper(DbImportExtensionMapper.NewInstance("qualitystatus_name", qualityStatusExtType)); //checked by Tax Editor ERMS1.1, Added by db management team (2x), checked by Tax Editor
121
122 ExtensionType cacheCitationExtType = getExtensionType(PesiTransformer.uuidExtCacheCitation, "cache_citation", "quality status", "cache_citation");
123 mapping.addMapper(DbImportExtensionMapper.NewInstance("cache_citation", cacheCitationExtType));
124
125 //flags
126 mapping.addMapper(DbImportMarkerMapper.NewInstance("tu_marine", ErmsTransformer.uuidMarkerMarine, "marine", "marine", "marine", null));
127 mapping.addMapper(DbImportMarkerMapper.NewInstance("tu_brackish", ErmsTransformer.uuidMarkerBrackish, "brackish", "brackish", "brackish", null));
128 mapping.addMapper(DbImportMarkerMapper.NewInstance("tu_fresh", ErmsTransformer.uuidMarkerFreshwater, "freshwater", "fresh", "fresh", null));
129 mapping.addMapper(DbImportMarkerMapper.NewInstance("tu_terrestrial", ErmsTransformer.uuidMarkerTerrestrial, "terrestrial", "terrestrial", "terrestrial", null));
130
131 //last action, species expert
132 ExtensionType speciesExpertNameExtType = getExtensionType(PesiTransformer.uuidExtSpeciesExpertName, "species expert name", "species expert name", "species expert name");
133 mapping.addMapper(DbImportExtensionMapper.NewInstance("ExpertName", speciesExpertNameExtType)); //according to sql script ExpertName maps to SpeciesExpertName in ERMS
134 AnnotationType lastActionDateType = getAnnotationType(DbLastActionMapper.uuidAnnotationTypeLastActionDate, "Last action date", "Last action date", null);
135 mapping.addMapper(DbImportAnnotationMapper.NewInstance("lastActionDate", lastActionDateType));
136 AnnotationType lastActionType = getAnnotationType(DbLastActionMapper.uuidAnnotationTypeLastAction, "Last action", "Last action", null);
137 MarkerType hasNoLastActionMarkerType = getMarkerType(DbLastActionMapper.uuidMarkerTypeHasNoLastAction, "has no last action", "No last action information available", "no last action");
138 mapping.addMapper(DbImportAnnotationMapper.NewInstance("lastAction", lastActionType, hasNoLastActionMarkerType));
139
140 //MAN authorshipCache => appendedPhrase
141 mapping.addMapper(DbImportMethodMapper.NewDefaultInstance(this, "appendedPhraseForMisapplications", ErmsImportState.class));
142
143 //titleCache compare
144 mapping.addMapper(DbImportMethodMapper.NewDefaultInstance(this, "testTitleCache", ErmsImportState.class));
145
146 //ignore
147 mapping.addMapper(DbIgnoreMapper.NewInstance("tu_sp", "included in rank/object creation, only needed for defining kingdom"));
148 mapping.addMapper(DbIgnoreMapper.NewInstance("tu_fossil", "tu_fossil implemented as foreign key"));
149
150 }
151 return mapping;
152 }
153
154 @Override
155 protected String getRecordQuery(ErmsImportConfigurator config) {
156 String strSelect = " SELECT tu.*, parent1.tu_name AS parent1name, parent2.tu_name AS parent2name, parent3.tu_name AS parent3name, parent4.tu_name AS parent4name, " +
157 " parent1.tu_rank AS parent1rank, parent2.tu_rank AS parent2rank, parent3.tu_rank AS parent3rank, " +
158 " status.status_id as status_id, status.status_name, fossil.fossil_name, qualitystatus.qualitystatus_name," +
159 " s.sessiondate lastActionDate, a.action_name lastAction, s.ExpertName ";
160 String strFrom = " FROM tu LEFT OUTER JOIN tu AS parent1 ON parent1.id = tu.tu_parent " +
161 " LEFT OUTER JOIN tu AS parent2 ON parent2.id = parent1.tu_parent " +
162 " LEFT OUTER JOIN tu AS parent3 ON parent2.tu_parent = parent3.id " +
163 " LEFT OUTER JOIN tu AS parent4 ON parent3.tu_parent = parent4.id " +
164 " LEFT OUTER JOIN status ON tu.tu_status = status.status_id " +
165 " LEFT OUTER JOIN fossil ON tu.tu_fossil = fossil.fossil_id " +
166 " LEFT OUTER JOIN qualitystatus ON tu.tu_qualitystatus = qualitystatus.id " +
167 " LEFT OUTER JOIN tu_sessions ts ON ts.tu_id = tu.id " +
168 " LEFT OUTER JOIN [sessions] s ON s.id = ts.session_id " +
169 " LEFT OUTER JOIN actions a ON a.id = ts.action_id ";
170 String strWhere = " WHERE ( tu.id IN (" + ID_LIST_TOKEN + ") )";
171 String strOrderBy = " ORDER BY tu.id, s.sessiondate DESC, a.id DESC ";
172 String strRecordQuery = strSelect + strFrom + strWhere + strOrderBy;
173 return strRecordQuery;
174 }
175
176 @Override
177 protected void doInvoke(ErmsImportState state) {
178 state.setAcceptedTaxaKeys(getAcceptedTaxaKeys(state));
179
180 //first path
181 super.doInvoke(state);
182 if(true){
183 logUnacceptReasons();
184 }
185 return;
186 }
187
188 Integer lastTaxonId = null;
189 @Override
190 protected boolean ignoreRecord(ResultSet rs) throws SQLException {
191 Integer id = rs.getInt("id");
192 boolean result = id.equals(lastTaxonId);
193 lastTaxonId = id;
194 return result;
195 }
196
197 private Set<Integer> getAcceptedTaxaKeys(ErmsImportState state) {
198 Set<Integer> result = new HashSet<>();
199 String idCol = " id ";
200 String tuFk = "tu_id";
201 String vernacularsTable = "vernaculars";
202 String distributionTable = "dr";
203 String notesTable = "notes";
204 String sql =
205 " SELECT id FROM tu WHERE tu_accfinal is NULL" //id of taxa not having accepted taxon
206 + " UNION SELECT DISTINCT tu_accfinal FROM tu " //fk to accepted taxon (either the accepted taxon or the taxon itself, if accepted)
207 + " UNION SELECT id FROM tu WHERE trim(tu.tu_unacceptreason) like 'misidentification' OR trim(tu.tu_unacceptreason) like 'misidentifications' OR "
208 + " tu.tu_unacceptreason like 'misapplied %%name' OR "
209 + " tu.tu_unacceptreason like '%%misapplication%%' OR "
210 + " tu.tu_unacceptreason like 'incorrect identification%%'" //Misapplications, see ErmsTransformer.getSynonymRelationTypesByKey
211 + " UNION SELECT syn.id FROM tu syn INNER JOIN tu acc ON syn.tu_accfinal = acc.id WHERE syn.id = acc.tu_parent AND acc.id <> syn.id " //see also ErmsTaxonRelationImport.isAccepted, there are some autonyms being the accepted taxon of there own parents
212 + " UNION SELECT DISTINCT %s FROM %s " //vernaculars
213 + " UNION SELECT DISTINCT %s FROM %s " //distributions
214 + " UNION SELECT DISTINCT %s FROM %s "; //notes
215 sql = String.format(sql,
216 tuFk, vernacularsTable,
217 tuFk, distributionTable,
218 tuFk, notesTable);
219 ResultSet rs = state.getConfig().getSource().getResultSet(sql);
220 try {
221 while (rs.next()){
222 Integer id;
223 id = rs.getInt(idCol.trim());
224 result.add(id);
225 }
226 return result;
227 } catch (SQLException e) {
228 e.printStackTrace();
229 throw new RuntimeException(e);
230 }
231 }
232
233 @Override
234 public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, ErmsImportState state) {
235 //currently no referencing objects needed
236 Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<>();
237 return result;
238 }
239
240 @Override
241 public TaxonBase<?> createObject(ResultSet rs, ErmsImportState state) throws SQLException {
242 int statusId = rs.getInt("status_id");
243 // Object accTaxonId = rs.getObject("tu_accfinal");
244 Integer meId = rs.getInt("id");
245
246 TaxonName taxonName = getTaxonName(rs, state);
247 fillTaxonName(taxonName, rs, state, meId);
248
249 //add original source for taxon name (taxon original source is added in mapper)
250 Reference citation = state.getTransactionalSourceReference();
251 addOriginalSource(rs, taxonName, "id", NAME_NAMESPACE, citation);
252
253 TaxonBase<?> result;
254 //handle accepted<-> synonym, we create more accepted taxa as we need them within the tree or to attache factual data
255 if (state.getAcceptedTaxaKeys().contains(meId)){
256 Taxon taxon = Taxon.NewInstance(taxonName, citation);
257 if (statusId != 1){
258 logger.info("Taxon created as taxon but has status <> 1 ("+statusId+"): " + meId);
259 handleNotAcceptedTaxon(taxon, statusId, state, rs);
260 }
261 result = taxon;
262 }else{
263 result = Synonym.NewInstance(taxonName, citation);
264 }
265
266 handleNameStatus(result.getName(), rs, state);
267 return result;
268 }
269
270 private void handleNameStatus(TaxonName name, ResultSet rs, ErmsImportState state) throws SQLException {
271 NomenclaturalStatusType nomStatus = null;
272 int tuStatus = rs.getInt("tu_status");
273 //the order is bottom up from SQL script as their values are overridden from top to bottom
274 if (tuStatus == 8){
275 //species inquirenda
276 nomStatus = getNomenclaturalStatusType(state, ErmsTransformer.uuidNomStatusSpeciesInquirenda, "species inquirenda", "species inquirenda", null, Language.LATIN(), null);
277 }else if (tuStatus == 7){
278 //temporary name
279 nomStatus = getNomenclaturalStatusType(state, PesiTransformer.uuidNomStatusTemporaryName, "temporary name", "temporary name", null, Language.ENGLISH(), null);
280 }else if (tuStatus == 6){
281 //nomen dubium
282 nomStatus = NomenclaturalStatusType.DOUBTFUL();
283 }else if (tuStatus == 5){
284 //"alternate representation"
285 nomStatus = getNomenclaturalStatusType(state, ErmsTransformer.uuidNomStatusAlternateRepresentation, "alternate representation", "alternate representation", null, Language.ENGLISH(), null);
286 }else if (tuStatus == 3){
287 //nomen nudum
288 nomStatus = NomenclaturalStatusType.NUDUM();
289 }
290 if (nomStatus == null){
291 //IN SQL Script it is set first by unacceptreason and then overriden if above tu_status exists
292 String unacceptReason = rs.getString("tu_unacceptreason");
293 try {
294 nomStatus = state.getTransformer().getNomenclaturalStatusByKey(unacceptReason);
295 } catch (UndefinedTransformerMethodException e) {logger.warn("Unhandled method");
296 }
297 }
298 if (nomStatus != null){
299 name.addStatus(nomStatus, null, null);
300 }
301 }
302
303 private TaxonName fillTaxonName(TaxonName taxonName, ResultSet rs, ErmsImportState state, Integer meId) throws SQLException {
304 String tuName = rs.getString("tu_name");
305 String displayName = rs.getString("tu_displayname").trim();
306
307 String parent1Name = rs.getString("parent1name");
308 Integer parent1Rank = rs.getInt("parent1rank");
309
310 String parent2Name = rs.getString("parent2name");
311 Integer parent2Rank = rs.getInt("parent2rank");
312
313 String parent3Name = rs.getString("parent3name");
314 Integer parent3Rank = rs.getInt("parent3rank");
315
316 String parent4Name = rs.getString("parent4name");
317
318 //set epithets
319 if (taxonName.isGenus() || taxonName.isSupraGeneric()){
320 taxonName.setGenusOrUninomial(tuName);
321 }else if (taxonName.isInfraGeneric()){
322 taxonName.setInfraGenericEpithet(tuName);
323 taxonName.setGenusOrUninomial(parent1Name);
324 }else if (taxonName.isSpecies()){
325 taxonName.setSpecificEpithet(tuName);
326 getGenusAndInfraGenus(parent1Name, parent2Name, parent1Rank, taxonName);
327 }else if (taxonName.isInfraSpecific()){
328 if (parent1Rank < 220){
329 handleException(parent1Rank, taxonName, displayName, meId);
330 }
331 taxonName.setInfraSpecificEpithet(tuName);
332 if (parent1Rank > 220){ //parent is still infraspecific
333 taxonName.setSpecificEpithet(parent2Name);
334 getGenusAndInfraGenus(parent3Name, parent4Name, parent3Rank, taxonName);
335 }else{
336 //default
337 taxonName.setSpecificEpithet(parent1Name);
338 getGenusAndInfraGenus(parent2Name, parent3Name, parent2Rank, taxonName);
339 }
340 }else if (taxonName.getRank()== null){
341 if ("Biota".equalsIgnoreCase(tuName)){
342 Rank rank = Rank.DOMAIN(); //should be Superdomain
343 taxonName.setRank(rank);
344 taxonName.setGenusOrUninomial(tuName);
345 }else{
346 String warning = "TaxonName has no rank. Use namecache.";
347 logger.warn(warning);
348 taxonName.setNameCache(tuName);
349 }
350 }
351
352 //e.g. Leucon [Platyhelminthes] ornatus
353 if (containsBrackets(displayName)){
354 taxonName.setNameCache(displayName);
355 logger.warn("Set name cache: " + displayName + "; id =" + meId);
356 }
357 if (!taxonName.getNameCache().equals(displayName)){
358 int pos = CdmUtils.diffIndex(taxonName.getNameCache(), displayName);
359 logger.warn("Computed name cache differs at "+pos+".\n Computed : " + taxonName.getNameCache()+"\n DisplayName: " +displayName);
360 taxonName.setNameCache(displayName, true);
361 }
362 taxonName.getTitleCache();
363 return taxonName;
364 }
365
366 @SuppressWarnings("unused") //used by MethodMapper
367 private static TaxonBase<?> appendedPhraseForMisapplications(ResultSet rs, ErmsImportState state) throws SQLException{
368 TaxonBase<?> taxon = (TaxonBase<?>)state.getRelatedObject(DbImportStateBase.CURRENT_OBJECT_NAMESPACE, DbImportStateBase.CURRENT_OBJECT_ID);
369 TaxonName taxonName = taxon.getName();
370 String unacceptreason = rs.getString("tu_unacceptreason");
371 RelationshipTermBase<?>[] rels = state.getTransformer().getSynonymRelationTypesByKey(unacceptreason, state);
372 if (rels[1]!= null && rels[1].equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
373 taxon.setAppendedPhrase(taxonName.getAuthorshipCache());
374 taxon.setSec(null);
375 taxonName.setAuthorshipCache(null, taxonName.isProtectedAuthorshipCache());
376 //TODO maybe some further authorship handling is needed if authors get parsed, but not very likely for MAN authorship
377 }
378 if(state.getUnhandledUnacceptReason() != null){
379 //to handle it hear is a workaround, as the real place where it is handled is DbImportSynonymMapper which is called ErmsTaxonRelationImport but where it is diffcult to aggregate logging data
380 addUnacceptReason(state.getUnhandledUnacceptReason());
381 }
382 return taxon;
383 }
384
385 private static void addUnacceptReason(String unhandledUnacceptReason) {
386 unhandledUnacceptReason = unhandledUnacceptReason.toLowerCase();
387 if (!unacceptReasons.keySet().contains(unhandledUnacceptReason)){
388 unacceptReasons.put(unhandledUnacceptReason, 1);
389 }else{
390 unacceptReasons.put(unhandledUnacceptReason, unacceptReasons.get(unhandledUnacceptReason)+1);
391 }
392 }
393
394 @SuppressWarnings("unused") //used by MethodMapper
395 private static TaxonBase<?> testTitleCache(ResultSet rs, ErmsImportState state) throws SQLException{
396 TaxonBase<?> taxon = (TaxonBase<?>)state.getRelatedObject(DbImportStateBase.CURRENT_OBJECT_NAMESPACE, DbImportStateBase.CURRENT_OBJECT_ID);
397 TaxonName taxonName = taxon.getName();
398 String displayName = rs.getString("tu_displayname");
399 displayName = displayName == null ? null : displayName.trim();
400 String titleCache = taxonName.resetTitleCache(); //calling titleCache should always be kept to have a computed titleCache in the CDM DB.
401 String expectedTitleCache = getExpectedTitleCache(rs);
402 //TODO check titleCache, but beware of autonyms
403 if (!titleCache.equals(expectedTitleCache)){
404 int pos = CdmUtils.diffIndex(titleCache, expectedTitleCache);
405 logger.warn("Computed title cache differs at "+pos+".\n Computed : " + titleCache + "\n DisplayName+Authority: " + expectedTitleCache);
406 taxonName.setNameCache(displayName, true);
407 }
408 return taxon;
409 }
410
411 //see also PesiErmsValidation.srcFullName()
412 private static String getExpectedTitleCache(ResultSet srcRs) throws SQLException {
413 String result;
414 String epi = srcRs.getString("tu_name");
415 epi = " a" + epi;
416 String display = srcRs.getString("tu_displayname");
417 String sp = srcRs.getString("tu_sp");
418 if (display.indexOf(epi) != display.lastIndexOf(epi) && !sp.startsWith("#2#")){ //homonym, animal
419 result = srcRs.getString("tu_displayname").replaceFirst(epi+" ", CdmUtils.concat(" ", " "+epi, srcRs.getString("tu_authority")))+" ";
420 }else{
421 result = CdmUtils.concat(" ", srcRs.getString("tu_displayname"), srcRs.getString("tu_authority"));
422 }
423 return result.trim();
424 }
425
426 private void handleNotAcceptedTaxon(Taxon taxon, int statusId, ErmsImportState state, ResultSet rs) throws SQLException {
427 ExtensionType notAccExtensionType = getExtensionType(state, ErmsTransformer.uuidErmsTaxonStatus, "ERMS taxon status", "ERMS taxon status", "status", null);
428 String statusName = rs.getString("status_name");
429
430 if (statusId > 1){
431 taxon.addExtension(statusName, notAccExtensionType);
432 }
433 }
434
435 private void handleException(Integer parentRank, TaxonName taxonName, String displayName, Integer meId) {
436 logger.warn("Parent of infra specific taxon is of higher rank ("+parentRank+") than species. Used nameCache: " + displayName + "; id=" + meId) ;
437 taxonName.setNameCache(displayName);
438 }
439
440 private boolean containsBrackets(String displayName) {
441 int index = displayName.indexOf("[");
442 return (index > -1);
443 }
444
445 private void getGenusAndInfraGenus(String parentName, String grandParentName, Integer parent1Rank, TaxonName taxonName) {
446 if (parent1Rank <220 && parent1Rank > 180){
447 //parent is infrageneric
448 taxonName.setInfraGenericEpithet(parentName);
449 taxonName.setGenusOrUninomial(grandParentName);
450 }else{
451 taxonName.setGenusOrUninomial(parentName);
452 }
453 }
454
455 /**
456 * Returns an empty Taxon Name instance according to the given rank and kingdom.
457 */
458 private TaxonName getTaxonName(ResultSet rs, ErmsImportState state) throws SQLException {
459 TaxonName result;
460 int kingdomId = parseKingdomId(rs);
461 Integer intRank = rs.getInt("tu_rank");
462
463 NomenclaturalCode nc = ErmsTransformer.kingdomId2NomCode(kingdomId);
464 Rank rank = null;
465 rank = state.getRank(intRank, kingdomId);
466
467 if (rank == null){
468 logger.warn("Rank is null. KingdomId: " + kingdomId + ", rankId: " + intRank);
469 }
470 if (nc != null){
471 result = nc.getNewTaxonNameInstance(rank);
472 }else{
473 result = TaxonNameFactory.NewNonViralInstance(rank);
474 }
475 //cache strategy
476 if (result.isZoological()){
477 TaxonNameDefaultCacheStrategy cacheStrategy = PesiTaxonExport.zooNameStrategy;
478 result.setCacheStrategy(cacheStrategy);
479 }
480
481 return result;
482 }
483
484 /**
485 * Returns the kingdom id by extracting it from the second character in the <code>tu_sp</code>
486 * attribute. If the attribute can not be parsed to a valid id <code>null</code>
487 * is returned. If the attribute is <code>null</code> the id of the record is returned.
488 * @param rs
489 * @return
490 * @throws SQLException
491 */
492 private int parseKingdomId(ResultSet rs) throws SQLException {
493 String treeString = rs.getString("tu_sp");
494 if (treeString != null){
495 if (StringUtils.isNotBlank(treeString) && treeString.length() > 1){
496 String strKingdom = treeString.substring(1,2);
497
498 if (! treeString.substring(0, 1).equals("#") && ! treeString.substring(2, 3).equals("#") ){
499 String message = "Tree string " + treeString + " has no recognized format";
500 logger.warn(message);
501 throw new RuntimeException(message);
502 }else{
503 try {
504 return Integer.valueOf(strKingdom);
505 } catch (NumberFormatException e) {
506 String message = "Kingdom string " + strKingdom + "could not be recognized as a valid number";
507 logger.warn(message);
508 throw new RuntimeException(message);
509 }
510 }
511 }else{
512 String message = "Tree string for kingdom recognition is to short: " + treeString;
513 logger.warn(message);
514 throw new RuntimeException(message);
515 }
516 }else{
517 int tu_id = rs.getInt("id");
518 return tu_id;
519 }
520 }
521
522 private void logUnacceptReasons() {
523 String logStr = "\n Unhandled unaccept reasons:\n===================";
524
525 while (!unacceptReasons.isEmpty()) {
526 int n = 0;
527 List<String> mostUsedStrings = new ArrayList<>();
528 for (Map.Entry<String, Integer> entry : unacceptReasons.entrySet()) {
529 if (entry.getValue() > n) {
530 mostUsedStrings = new ArrayList<>();
531 mostUsedStrings.add(entry.getKey());
532 n = entry.getValue();
533 } else if (entry.getValue() == n) {
534 mostUsedStrings.add(entry.getKey());
535 } else {
536 //neglect
537 }
538 }
539 mostUsedStrings.sort(new StringComparator());
540 logStr += "\n " + String.valueOf(n);
541 for (String str : mostUsedStrings) {
542 logStr += "\n "+ str;
543 unacceptReasons.remove(str);
544 }
545 }
546 logger.warn(logStr);
547
548 }
549
550 @Override
551 protected boolean doCheck(ErmsImportState state){
552 IOValidator<ErmsImportState> validator = new ErmsTaxonImportValidator();
553 return validator.validate(state);
554 }
555
556 @Override
557 protected boolean isIgnore(ErmsImportState state){
558 return ! state.getConfig().isDoTaxa();
559 }
560 }