Project

General

Profile

Download (41.4 KB) Statistics
| Branch: | Revision:
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.berlinModel.in;
11

    
12
import java.io.IOException;
13
import java.net.URI;
14
import java.net.URISyntaxException;
15
import java.sql.ResultSet;
16
import java.sql.SQLException;
17
import java.util.HashMap;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.UUID;
23

    
24
import javax.validation.constraints.NotNull;
25

    
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.http.HttpException;
28
import org.apache.log4j.Logger;
29
import org.springframework.stereotype.Component;
30

    
31
import au.com.bytecode.opencsv.CSVReader;
32
import eu.etaxonomy.cdm.common.CdmUtils;
33
import eu.etaxonomy.cdm.common.UTF8;
34
import eu.etaxonomy.cdm.common.media.ImageInfo;
35
import eu.etaxonomy.cdm.database.update.DatabaseTypeNotSupportedException;
36
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
37
import eu.etaxonomy.cdm.io.berlinModel.in.validation.BerlinModelFactsImportValidator;
38
import eu.etaxonomy.cdm.io.common.IOValidator;
39
import eu.etaxonomy.cdm.io.common.ResultSetPartitioner;
40
import eu.etaxonomy.cdm.io.common.Source;
41
import eu.etaxonomy.cdm.model.agent.AgentBase;
42
import eu.etaxonomy.cdm.model.agent.Person;
43
import eu.etaxonomy.cdm.model.agent.Team;
44
import eu.etaxonomy.cdm.model.common.Annotation;
45
import eu.etaxonomy.cdm.model.common.AnnotationType;
46
import eu.etaxonomy.cdm.model.common.CdmBase;
47
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
48
import eu.etaxonomy.cdm.model.common.Language;
49
import eu.etaxonomy.cdm.model.common.LanguageString;
50
import eu.etaxonomy.cdm.model.common.Marker;
51
import eu.etaxonomy.cdm.model.common.MarkerType;
52
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
53
import eu.etaxonomy.cdm.model.common.Representation;
54
import eu.etaxonomy.cdm.model.common.TermBase;
55
import eu.etaxonomy.cdm.model.common.TermType;
56
import eu.etaxonomy.cdm.model.common.TermVocabulary;
57
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
58
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
59
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
60
import eu.etaxonomy.cdm.model.description.Distribution;
61
import eu.etaxonomy.cdm.model.description.Feature;
62
import eu.etaxonomy.cdm.model.description.FeatureNode;
63
import eu.etaxonomy.cdm.model.description.FeatureTree;
64
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
65
import eu.etaxonomy.cdm.model.description.TaxonDescription;
66
import eu.etaxonomy.cdm.model.description.TextData;
67
import eu.etaxonomy.cdm.model.location.NamedArea;
68
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
69
import eu.etaxonomy.cdm.model.location.NamedAreaType;
70
import eu.etaxonomy.cdm.model.media.ImageFile;
71
import eu.etaxonomy.cdm.model.media.Media;
72
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
73
import eu.etaxonomy.cdm.model.media.Rights;
74
import eu.etaxonomy.cdm.model.media.RightsType;
75
import eu.etaxonomy.cdm.model.reference.Reference;
76
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
77
import eu.etaxonomy.cdm.model.taxon.Taxon;
78
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
79
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
80
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
81

    
82
/**
83
 * @author a.mueller
84
 * @since 20.03.2008
85
 */
86
@Component
87
public class BerlinModelFactsImport  extends BerlinModelImportBase {
88
    private static final long serialVersionUID = 4095154818163504795L;
89

    
90
    private static final Logger logger = Logger.getLogger(BerlinModelFactsImport.class);
91

    
92
	public static final String NAMESPACE = "Fact";
93

    
94
	public static final String SEQUENCE_PREFIX = "ORDER: ";
95

    
96
	private int modCount = 10000;
97
	private static final String pluralString = "facts";
98
	private static final String dbTableName = "Fact";
99

    
100
	//FIXME don't use as class variable
101
	private Map<Integer, Feature> featureMap;
102

    
103
	public BerlinModelFactsImport(){
104
		super(dbTableName, pluralString);
105
	}
106

    
107

    
108
	private TermVocabulary<Feature> getFeatureVocabulary(){
109
	    TermVocabulary<Feature> newVoc = TermVocabulary.NewInstance(TermType.Feature, "Berlin Model Import Feature Vocabulary", "Berlin Model Import Feature Vocabulary", null, null);
110
	    getVocabularyService().save(newVoc);
111

    
112
	    return newVoc;
113

    
114
//	    try {
115
//			//TODO work around until service method works
116
//			TermVocabulary<Feature> featureVocabulary =  BerlinModelTransformer.factCategory2Feature(1).getVocabulary();
117
//			//TermVocabulary<Feature> vocabulary = getTermService().getVocabulary(vocabularyUuid);
118
//			return featureVocabulary;
119
//		} catch (UnknownCdmTypeException e) {
120
//			logger.error("Feature vocabulary not available. New vocabulary created");
121
//			return TermVocabulary.NewInstance(TermType.Feature, "User Defined Feature Vocabulary", "User Defined Feature Vocabulary", null, null);
122
//		}
123
	}
124

    
125
	private Map<Integer, Feature>  invokeFactCategories(BerlinModelImportState state){
126

    
127
		Map<Integer, Feature>  result = state.getConfig().getFeatureMap();
128
		Source source = state.getConfig().getSource();
129
        boolean createFeatureTree = state.getConfig().isSalvador();  //for some reason feature tree creation does not work for salavdor
130

    
131
        FeatureTree featureTree = (!createFeatureTree) ? null : FeatureTree.NewInstance(state.getConfig().getFeatureTreeUuid());
132
        if (featureTree!= null && createFeatureTree){
133
            featureTree.setTitleCache(state.getConfig().getFeatureTreeTitle(), true);
134
        }
135

    
136
        try {
137
			//get data from database
138
			String strQuery =
139
					" SELECT FactCategory.* " +
140
					" FROM FactCategory "+
141
                    " WHERE (1=1)";
142
			if (state.getConfig().isSalvador()){
143
			    strQuery += " AND " + state.getConfig().getFactFilter().replace("factCategoryFk", "factCategoryId");
144
			}
145

    
146
			ResultSet rs = source.getResultSet(strQuery) ;
147

    
148
			TermVocabulary<Feature> featureVocabulary = getFeatureVocabulary();
149
			int i = 0;
150
			//for each reference
151
			while (rs.next()){
152

    
153
				if ((i++ % modCount) == 0 && i!= 1 ){ logger.info("FactCategories handled: " + (i-1));}
154

    
155
				int factCategoryId = rs.getInt("factCategoryId");
156
				String factCategory = rs.getString("factCategory");
157

    
158
				Feature feature;
159
				try {
160
					feature = BerlinModelTransformer.factCategory2Feature(factCategoryId);
161
				} catch (UnknownCdmTypeException e) {
162
					UUID featureUuid = null;
163
					featureUuid = BerlinModelTransformer.getFeatureUuid(String.valueOf(factCategoryId+"-"+factCategory));
164
					if (featureUuid == null){
165
						logger.warn("New Feature (FactCategoryId: " + factCategoryId + ")");
166
						featureUuid = UUID.randomUUID();
167
					}
168
					feature = getFeature(state, featureUuid, factCategory, factCategory, null, featureVocabulary);
169
					if (state.getConfig().isSalvador()){
170
					    adaptNewSalvadorFeature(factCategoryId, feature);
171
					}
172
                    //id
173
                    doId(state, feature, factCategoryId, "FactCategory");
174

    
175
					//TODO
176
//					MaxFactNumber	int	Checked
177
//					ExtensionTableName	varchar(100)	Checked
178
//					Description	nvarchar(1000)	Checked
179
//					locExtensionFormName	nvarchar(80)	Checked
180
//					RankRestrictionFk	int	Checked
181
				}
182

    
183
				result.put(factCategoryId, feature);
184
				if (createFeatureTree && isPublicFeature(factCategoryId)){
185
				    featureTree.getRoot().addChild(FeatureNode.NewInstance(feature));
186
				}
187
			}
188
			if (createFeatureTree){
189
			    featureTree.getRoot().addChild(FeatureNode.NewInstance(Feature.DISTRIBUTION()),2);
190
                featureTree.getRoot().addChild(FeatureNode.NewInstance(Feature.NOTES()), featureTree.getRoot().getChildCount()-1);
191
			    getFeatureTreeService().save(featureTree);
192
			}
193
			return result;
194
		} catch (SQLException e) {
195
			logger.error("SQLException:" +  e);
196
			return null;
197
		}
198
	}
199

    
200

    
201
    /**
202
     * @param factCategoryId
203
     * @param feature
204
     */
205
    private void adaptNewSalvadorFeature(int factCategoryId, Feature feature) {
206
        if (factCategoryId == 306){
207
            addSpanishRepresentationLabel(feature, "Nombre(s) común(es)");
208
        } else if (factCategoryId == 307){
209
            addSpanishRepresentationLabel(feature, "Muestras de herbario");
210
        } else if (factCategoryId == 310){
211
            addEnglishFactCategoryName(feature, "Other references for taxon");
212
        } else if (factCategoryId == 309){
213
            addEnglishFactCategoryName(feature, "Report (reference) for El Salvador");
214
        } else if (factCategoryId == 311){
215
            addEnglishFactCategoryName(feature, "Taxon illustration references");
216
        } else if (factCategoryId == 312){
217
            addSpanishRepresentationLabel(feature, "Imágen");
218
        } else if (factCategoryId == 350){
219
            addSpanishRepresentationLabel(feature, "Descripción");
220
        } else if (factCategoryId == 303){
221
            addEnglishFactCategoryName(feature, "General distribution");
222
        } else if (factCategoryId == 2000){
223
            addEnglishFactCategoryName(feature, "Habitat in El Salvador");
224
        } else if (factCategoryId == 302){
225
            addSpanishRepresentationLabel(feature, "Usos");
226
        } else if (factCategoryId == 1800){
227
            addEnglishFactCategoryName(feature, "Specimen notes");
228
        } else if (factCategoryId == 1900){
229
            addEnglishFactCategoryName(feature, "Editorial notes");
230
        }
231

    
232
    }
233

    
234

    
235
    /**
236
     * @param feature
237
     * @param string
238
     */
239
    private void addSpanishRepresentationLabel(TermBase term, String label) {
240
        term.getRepresentations().add(Representation.NewInstance(label, label, null, Language.SPANISH_CASTILIAN()));
241
    }
242

    
243
    /**
244
     * @param feature
245
     * @param string
246
     */
247
    private void addEnglishFactCategoryName(Feature feature, String label) {
248
        feature.getRepresentations().iterator().next().setLanguage(Language.SPANISH_CASTILIAN());
249
        feature.getRepresentations().add(Representation.NewInstance(label, label, null, Language.ENGLISH()));
250
    }
251

    
252

    
253
    @Override
254
	protected void doInvoke(BerlinModelImportState state) {
255
        if (state.getConfig().isSalvador()){
256
            invokeSpanishTermLabels();
257
        }
258
		featureMap = invokeFactCategories(state);
259
		super.doInvoke(state);
260
		return;
261
	}
262

    
263

    
264
	/**
265
     *
266
     */
267
    private void invokeSpanishTermLabels() {
268
        addSpanishRepresentationLabel(Feature.NOTES(), "Notas");
269
        addSpanishRepresentationLabel(NamedAreaLevel.DEPARTMENT(), "Departamento");
270
        addSpanishRepresentationLabel(PresenceAbsenceTerm.NATIVE(), "nativo");
271
        addSpanishRepresentationLabel(PresenceAbsenceTerm.CULTIVATED(), "cultivado");
272
        addSpanishRepresentationLabel(PresenceAbsenceTerm.PRESENT(), "presente");
273

    
274

    
275
    }
276

    
277

    
278
    @Override
279
	protected String getIdQuery(BerlinModelImportState state) {
280
		String result = super.getIdQuery(state);
281
		if (StringUtils.isNotBlank(state.getConfig().getFactFilter())){
282
			result += " WHERE " + state.getConfig().getFactFilter();
283
		}else{
284
			result = super.getIdQuery(state);
285
		}
286
		result += getOrderBy(state.getConfig());
287
		return result;
288
	}
289

    
290
	@Override
291
	protected String getRecordQuery(BerlinModelImportConfigurator config) {
292
			String strQuery =
293
					" SELECT Fact.*, PTaxon.RIdentifier as taxonId, RefDetail.Details " +
294
					" FROM Fact " +
295
                      	" INNER JOIN PTaxon ON Fact.PTNameFk = PTaxon.PTNameFk AND Fact.PTRefFk = PTaxon.PTRefFk " +
296
                      	" LEFT OUTER JOIN RefDetail ON Fact.FactRefDetailFk = RefDetail.RefDetailId AND Fact.FactRefFk = RefDetail.RefFk " +
297
              	" WHERE (FactId IN (" + ID_LIST_TOKEN + "))";
298
			    strQuery += getOrderBy(config);
299

    
300
		return strQuery;
301
	}
302

    
303

    
304
	private String getOrderBy(BerlinModelImportConfigurator config) {
305
		String result;
306
		try{
307
			if (config.getSource().checkColumnExists("Fact", "Sequence")){
308
				result = " ORDER By Fact.Sequence, Fact.FactId";
309
				if (config.isSalvador()){
310
				    result = " ORDER By Fact.FactCategoryFk, Fact.Sequence, Fact.FactId";
311
				}
312
			}else{
313
				result = " ORDER By Fact.FactId";
314
			}
315
		} catch (DatabaseTypeNotSupportedException e) {
316
			logger.info("checkColumnExists not supported");
317
			result = " ORDER By Fact.FactId";
318
		}
319
		return result;
320
	}
321

    
322
	@Override
323
	public boolean doPartition(ResultSetPartitioner partitioner, BerlinModelImportState state) {
324
		boolean success = true ;
325
		BerlinModelImportConfigurator config = state.getConfig();
326
		Set<TaxonBase> taxaToSave = new HashSet<TaxonBase>();
327
		Map<String, TaxonBase> taxonMap = partitioner.getObjectMap(BerlinModelTaxonImport.NAMESPACE);
328
		Map<String, Reference> refMap = partitioner.getObjectMap(BerlinModelReferenceImport.REFERENCE_NAMESPACE);
329

    
330
		ResultSet rs = partitioner.getResultSet();
331

    
332
		Reference sourceRef = state.getTransactionalSourceReference();
333

    
334
		try{
335
			int i = 0;
336
			//for each fact
337
			while (rs.next()){
338
				try{
339
					if ((i++ % modCount) == 0){ logger.info("Facts handled: " + (i-1));}
340

    
341
					int factId = rs.getInt("factId");
342
					Integer taxonId = nullSafeInt(rs, "taxonId");
343
					Integer factRefFkInt = nullSafeInt(rs, "factRefFk");
344
					Integer categoryFkInt = nullSafeInt(rs, "factCategoryFk");
345
					String details = rs.getString("Details");
346
					String fact = CdmUtils.Nz(rs.getString("Fact"));
347
					String notes = CdmUtils.Nz(rs.getString("notes"));
348
					Boolean doubtfulFlag = rs.getBoolean("DoubtfulFlag");
349

    
350
					TaxonBase<?> taxonBase = getTaxon(taxonMap, taxonId, taxonId);
351
					Feature feature = getFeature(featureMap, categoryFkInt) ;
352

    
353
					if (taxonBase == null){
354
						logger.warn("Taxon for Fact " + factId + " does not exist in store");
355
						success = false;
356
					}else{
357
						TaxonDescription taxonDescription;
358
						if ( (taxonDescription = getMyTaxonDescripion(taxonBase, state, categoryFkInt, taxonId, factId, fact, sourceRef)) == null){
359
							success = false;
360
							continue;
361
						}
362

    
363
						//textData
364
						TextData textData = null;
365
						boolean newTextData = true;
366

    
367
						// For Cichorieae DB: If fact category is 31 (Systematics) and there is already a Systematics TextData
368
						// description element append the fact text to the existing TextData
369
						if(categoryFkInt.equals(31)) {
370
							Set<DescriptionElementBase> descriptionElements = taxonDescription.getElements();
371
							for (DescriptionElementBase descriptionElement : descriptionElements) {
372
								String featureString = descriptionElement.getFeature().getRepresentation(Language.DEFAULT()).getLabel();
373
								if (descriptionElement instanceof TextData && featureString.equals("Systematics")) { // TODO: test
374
									textData = (TextData)descriptionElement;
375
									String factTextStr = textData.getText(Language.DEFAULT());
376
									// FIXME: Removing newlines doesn't work
377
									if (factTextStr.contains("\\r\\n")) {
378
										factTextStr = factTextStr.replaceAll("\\r\\n","");
379
									}
380
									StringBuilder factText = new StringBuilder(factTextStr);
381
									factText.append(fact);
382
									fact = factText.toString();
383
									newTextData = false;
384
									break;
385
								}
386
							}
387
						}
388

    
389
						if (taxonDescription.isImageGallery()){
390
						    newTextData = false;
391
						    textData = (TextData)taxonDescription.getElements().iterator().next();
392
						}
393
						if(newTextData == true)	{
394
							textData = TextData.NewInstance();
395
						}
396

    
397

    
398
						//for diptera database
399
						if (categoryFkInt.equals(99) && notes.contains("<OriginalName>")){
400
							fact = notes + ": " +  fact ;
401
						}
402
						//for E+M maps
403
						if (categoryFkInt.equals(14) && state.getConfig().isRemoveHttpMapsAnchor() && fact.contains("<a href")){
404
							//example <a href="http://euromed.luomus.fi/euromed_map.php?taxon=280629&size=medium">distribution</a>
405
							fact = fact.replace("<a href=\"", "").replace("\">distribution</a>", "");
406
						}
407

    
408
						//TODO textData.putText(fact, bmiConfig.getFactLanguage());  //doesn't work because  bmiConfig.getFactLanguage() is not not a persistent Language Object
409
						//throws  in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.common.Language; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.common.Language
410

    
411
						Language lang = Language.DEFAULT();
412
						if (state.getConfig().isSalvador()){
413
						    lang = getSalvadorFactLanguage(categoryFkInt);
414
						}
415
						if (! taxonDescription.isImageGallery()){
416
							textData.putText(lang, fact);
417
							textData.setFeature(feature);
418
						}
419

    
420
						DescriptionElementBase deb = textData;
421

    
422
						if (state.getConfig().isSalvador()){
423
						    if (categoryFkInt == 306){
424
						        NamedArea area = null;  // for now we do not set an area as it can not be disabled in dataportals via css yet
425
						        deb = CommonTaxonName.NewInstance(fact, Language.SPANISH_CASTILIAN(), area);
426
						    }else if (categoryFkInt == 307){
427
						        Distribution salvadorDistribution = salvadorDistributionFromMuestrasDeHerbar((Taxon)taxonBase, fact);
428
						        if (salvadorDistribution != null){
429
						            //id
430
						            doId(state, salvadorDistribution, factId, "Fact");
431
						            mergeSalvadorDistribution(taxonDescription, salvadorDistribution);
432
						        }
433
						    }
434
						}
435

    
436
						//reference
437
						Reference citation = null;
438
						String factRefFk = String.valueOf(factRefFkInt);
439
						if (factRefFkInt != null){
440
							citation = refMap.get(factRefFk);
441
						}
442
						if (citation == null && (factRefFkInt != null)){
443
							logger.warn("Citation not found in referenceMap: " + factRefFk);
444
							success = false;
445
						}
446
						if (citation != null || StringUtils.isNotBlank(details)){
447
							DescriptionElementSource originalSource = DescriptionElementSource.NewPrimarySourceInstance(citation, details);
448
							deb.addSource(originalSource);
449
						}
450
						taxonDescription.addElement(deb);
451
						//doubtfulFlag
452
						if (doubtfulFlag){
453
							deb.addMarker(Marker.NewInstance(MarkerType.IS_DOUBTFUL(), true));
454
						}
455
						//publisheFlag
456
						String strPublishFlag = "publishFlag";
457
						boolean publishFlagExists = state.getConfig().getSource().checkColumnExists(dbTableName, strPublishFlag);
458
						if (publishFlagExists){
459
							Boolean publishFlag = rs.getBoolean(strPublishFlag);
460
							if (publishFlag == false){
461
							    deb.addMarker(Marker.NewInstance(MarkerType.PUBLISH(), publishFlag));
462
							}
463
						}
464

    
465
						//Sequence
466
						Integer sequence = rs.getInt("Sequence");
467
						if (sequence != 999){
468
							String strSequence = String.valueOf(sequence);
469
							strSequence = SEQUENCE_PREFIX + strSequence;
470
							//TODO make it an Extension when possible
471
							//Extension datesExtension = Extension.NewInstance(textData, strSequence, ExtensionType.ORDER());
472
							Annotation annotation = Annotation.NewInstance(strSequence, AnnotationType.TECHNICAL(), Language.ENGLISH());
473
							deb.addAnnotation(annotation);
474
						}
475

    
476
						//						if (categoryFkObj == FACT_DESCRIPTION){
477
	//						//;
478
	//					}else if (categoryFkObj == FACT_OBSERVATION){
479
	//						//;
480
	//					}else if (categoryFkObj == FACT_DISTRIBUTION_EM){
481
	//						//
482
	//					}else {
483
	//						//TODO
484
	//						//logger.warn("FactCategory " + categoryFk + " not yet implemented");
485
	//					}
486

    
487
						//notes
488
						doCreatedUpdatedNotes(state, deb, rs);
489
						doId(state, deb, factId, "Fact");
490

    
491
						//TODO
492
						//Designation References -> unclear how to map to CDM
493

    
494

    
495
						//sequence -> textData is not an identifiable entity therefore extensions are not possible
496
						//fact category better
497

    
498
						taxaToSave.add(taxonBase);
499
					}
500
				} catch (Exception re){
501
					logger.error("An exception occurred during the facts import");
502
					re.printStackTrace();
503
					success = false;
504
				}
505
				//put
506
			}
507
			logger.info("Facts handled: " + (i-1));
508
			logger.info("Taxa to save: " + taxaToSave.size());
509
			getTaxonService().save(taxaToSave);
510
		}catch(SQLException e){
511
			throw new RuntimeException(e);
512
		}
513
		return success;
514
	}
515

    
516

    
517
	/**
518
     * @param taxonDescription
519
     * @param salvadorDistribution
520
     */
521
    private void mergeSalvadorDistribution(TaxonDescription taxonDescription,
522
            @NotNull Distribution newDistribution) {
523

    
524
        Distribution existingDistribution = null;
525
        for (DescriptionElementBase deb : taxonDescription.getElements()){
526
            if (deb.isInstanceOf(Distribution.class)){
527
                Distribution distribution = CdmBase.deproxy(deb, Distribution.class);
528
                if (distribution.getArea() != null && distribution.getArea().equals(newDistribution.getArea())){
529
                    existingDistribution = distribution;
530
                    break;
531
                }else if (distribution.getArea() == null){
532
                    logger.warn("Area for distribution is null: " + distribution.getUuid());
533
                }
534
            }
535
        }
536
        if (existingDistribution == null){
537
            taxonDescription.addElement(newDistribution);
538
        }else if(!existingDistribution.getStatus().equals(newDistribution.getStatus())){
539
            //should not happen
540
            logger.warn("Taxon has areas with different distribution states: " + taxonDescription.getTaxon().getTitleCache());
541
        }else{
542
            //do nothing, distribution already exists
543
        }
544
    }
545

    
546

    
547
    private Map<String, NamedArea> salvadorAreaMap = null;
548
    private Distribution salvadorDistributionFromMuestrasDeHerbar(Taxon taxon, String fact) {
549
        if (salvadorAreaMap == null){
550
            salvadorAreaMap = new HashMap<>();
551
            TermVocabulary<NamedArea> salvadorAreas = getVocabulary(TermType.NamedArea, BerlinModelTransformer.uuidSalvadorAreas,
552
                    "Salvador areas", "Salvador areas", null, null, true, NamedArea.NewInstance());
553
            getVocabularyService().save(salvadorAreas);
554
        }
555
        Distribution result = null;
556
        String[] areaStrings = fact.split(":");
557
        if (areaStrings.length > 1){
558
            String areaString = areaStrings[0];
559
            NamedArea area = salvadorAreaMap.get(areaString);
560
            if (area == null){
561
                logger.info("Added Salvador area: " + areaString);
562
                TermVocabulary<NamedArea> voc = getVocabulary(TermType.NamedArea, BerlinModelTransformer.uuidSalvadorAreas,
563
                        "Salvador departments", "Salvador departments", null, null, true, NamedArea.NewInstance());
564
                if (voc.getRepresentation(Language.SPANISH_CASTILIAN()) == null){
565
                    voc.addRepresentation(Representation.NewInstance("Salvador departamentos", "Salvador departamentos", "dep.", Language.SPANISH_CASTILIAN()));
566
                    getVocabularyService().saveOrUpdate(voc);
567
                }
568
                NamedArea newArea = NamedArea.NewInstance(areaString, areaString, null);
569
                newArea.getRepresentations().iterator().next().setLanguage(Language.SPANISH_CASTILIAN());
570
                newArea.setLevel(NamedAreaLevel.DEPARTMENT());
571
                newArea.setType(NamedAreaType.ADMINISTRATION_AREA());
572
                voc.addTerm(newArea);
573
                getTermService().saveOrUpdate(newArea);
574
                salvadorAreaMap.put(areaString, newArea);
575
                area = newArea;
576
            }
577
            PresenceAbsenceTerm state = getSalvadorDistributionState(taxon);
578
            result = Distribution.NewInstance(area, state);
579
            return result;
580
        }else{
581
            return null;
582
        }
583
    }
584

    
585
    private PresenceAbsenceTerm getSalvadorDistributionState(Taxon taxon) {
586
        boolean hasGlobalDist = false;
587
        for (TaxonDescription desc : taxon.getDescriptions()){
588
            for (DescriptionElementBase deb : desc.getElements()){
589
                if (deb.getFeature().getUuid().equals(BerlinModelTransformer.uuidFeatureDistributionGlobal)){
590
                    hasGlobalDist = true;
591
                    TextData textData = CdmBase.deproxy(deb, TextData.class);
592
                    for (LanguageString text : textData.getMultilanguageText().values()){
593
                        if (text.getText().contains("El Salvador")){
594
                            return PresenceAbsenceTerm.NATIVE();
595
                        }
596
                    }
597
                }
598
            }
599
        }
600
        if (!hasGlobalDist){
601
            logger.warn("No global distribution found: " + taxon.getTitleCache());
602
        }
603
        return hasGlobalDist ? PresenceAbsenceTerm.CULTIVATED(): PresenceAbsenceTerm.PRESENT();
604
    }
605

    
606

    
607
    /**
608
     * @param factId
609
     * @return
610
     */
611
    private Language getSalvadorFactLanguage(int categoryFkInt) {
612
        if (categoryFkInt == 350){
613
            return Language.ENGLISH();
614
        }else if (categoryFkInt == 1800 || categoryFkInt == 1900){
615
            return Language.UNDETERMINED();
616
        }
617
        return Language.SPANISH_CASTILIAN();
618
    }
619

    
620

    
621
    private TaxonDescription getMyTaxonDescripion(TaxonBase taxonBase, BerlinModelImportState state, Integer categoryFk, Integer taxonId, int factId, String fact, Reference sourceRef) {
622
		Taxon taxon = null;
623
		if ( taxonBase instanceof Taxon ) {
624
			taxon = (Taxon) taxonBase;
625
		}else{
626
			logger.warn("TaxonBase " + (taxonId==null?"(null)":taxonId) + " for Fact " + factId + " was not of type Taxon but: " + taxonBase.getClass().getSimpleName());
627
			return null;
628
		}
629

    
630
		TaxonDescription taxonDescription = null;
631
		Set<TaxonDescription> descriptionSet= taxon.getDescriptions();
632

    
633
		Media media = null;
634
		//for diptera / salvador images
635
		if (categoryFk == 51 || categoryFk == 312){  //TODO check also FactCategory string
636
			media = Media.NewInstance();
637
			taxonDescription = makeImage(state, fact, media, descriptionSet, taxon);
638

    
639
			if (taxonDescription == null){
640
				return null;
641
			}
642

    
643
			TextData textData = null;
644
			for (DescriptionElementBase el:  taxonDescription.getElements()){
645
				if (el.isInstanceOf(TextData.class)){
646
					textData = CdmBase.deproxy(el, TextData.class);
647
				}
648
			}
649
			if (textData == null){
650
				textData = TextData.NewInstance(Feature.IMAGE());
651
				taxonDescription.addElement(textData);
652
			}
653
			textData.addMedia(media);
654
		}
655
		//all others (no image) -> getDescription
656
		else{
657
			boolean isPublic = isPublicFeature(categoryFk);
658
		    for (TaxonDescription desc: descriptionSet){
659

    
660
			    if (! desc.isImageGallery()){
661
					if (state.getConfig().isSalvador()){
662
					    if (desc.isDefault() && isPublic || !desc.isDefault() && !isPublic){
663
					        taxonDescription = desc;
664
					        break;
665
					    }
666
					}else{
667
					    taxonDescription = desc;
668
					    break;
669
					}
670
				}
671
			}
672
			if (taxonDescription == null){
673
				taxonDescription = TaxonDescription.NewInstance();
674
				taxonDescription.setTitleCache(sourceRef == null ? null : sourceRef.getTitleCache(), true);
675
				if (state.getConfig().isSalvador()){
676
				    String title = "Factual data for " + taxon.getName().getTitleCache();
677
				    if (isPublic){
678
				        taxonDescription.setDefault(isPublic);
679
				    }else{
680
				        title = "Non public f" + title.substring(1);
681
				    }
682
				    taxonDescription.setTitleCache(title, true);
683
				}
684
				taxon.addDescription(taxonDescription);
685
			}
686
		}
687
		return taxonDescription;
688
	}
689

    
690

    
691
    /**
692
     * @param categoryFk
693
     * @return
694
     */
695
    private boolean isPublicFeature(Integer categoryFk) {
696
        return ! (categoryFk == 1800 || categoryFk == 1900 || categoryFk == 2000);
697
    }
698

    
699

    
700
	@Override
701
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
702
		String nameSpace;
703
		Class<?> cdmClass;
704
		Set<String> idSet;
705
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
706

    
707
		try{
708
			Set<String> taxonIdSet = new HashSet<String>();
709
			Set<String> referenceIdSet = new HashSet<String>();
710
			Set<String> refDetailIdSet = new HashSet<String>();
711
			while (rs.next()){
712
				handleForeignKey(rs, taxonIdSet, "taxonId");
713
				handleForeignKey(rs, referenceIdSet, "FactRefFk");
714
				handleForeignKey(rs, referenceIdSet, "PTDesignationRefFk");
715
				handleForeignKey(rs, refDetailIdSet, "FactRefDetailFk");
716
				handleForeignKey(rs, refDetailIdSet, "PTDesignationRefDetailFk");
717
		}
718

    
719
			//taxon map
720
			nameSpace = BerlinModelTaxonImport.NAMESPACE;
721
			cdmClass = TaxonBase.class;
722
			idSet = taxonIdSet;
723
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
724
			result.put(nameSpace, taxonMap);
725

    
726
			//reference map
727
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
728
			cdmClass = Reference.class;
729
			idSet = referenceIdSet;
730
			Map<String, Reference> referenceMap = (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
731
			result.put(nameSpace, referenceMap);
732

    
733
			//refDetail map
734
			nameSpace = BerlinModelRefDetailImport.REFDETAIL_NAMESPACE;
735
			cdmClass = Reference.class;
736
			idSet = refDetailIdSet;
737
			Map<String, Reference> refDetailMap= (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
738
			result.put(nameSpace, refDetailMap);
739

    
740
		} catch (SQLException e) {
741
			throw new RuntimeException(e);
742
	}
743
		return result;
744
	}
745

    
746

    
747
	/**
748
	 * @param state
749
	 * @param media
750
	 * @param media
751
	 * @param descriptionSet
752
	 * @throws URISyntaxException
753
	 *
754
	 */
755
	private TaxonDescription makeImage(BerlinModelImportState state, String fact, Media media, Set<TaxonDescription> descriptionSet, Taxon taxon) {
756
		TaxonDescription taxonDescription = null;
757
		try {
758
	        Reference sourceRef = state.getTransactionalSourceReference();
759
    		URI uri;
760
    		URI thumbUri;
761
    		if (state.getConfig().isSalvador()){
762
    		    String thumbs = "thumbs/";
763
    		    String uriStrFormat = "http://media.e-taxonomy.eu/salvador/berendsohn-et-al-%s/%s.jpg";
764
    		    Integer intFact = Integer.valueOf(fact);
765
    		    String vol = "2009";
766
    		    int page = intFact + 249;
767
                if (intFact >= 263){
768
    		        vol = "2016";
769
    		        page = intFact - (intFact < 403 ? 95 : 94);
770
    		    }else if (intFact >= 142){
771
    		        vol = "2012";
772
    		        page = intFact + (intFact < 255 ? 3 : 4);
773
    		    }
774

    
775
                String title = getSalvadorImageTitle(intFact, vol);
776
                media.putTitle(Language.LATIN(), title);
777
                String description = getSalvadorImageDescription(intFact);
778
                media.putDescription(Language.SPANISH_CASTILIAN(), description);
779

    
780
    		    Reference ref = getSalvadorReference(vol);
781
    		    String originalName = getSalvadorImageNameInfo(intFact);
782
    		    IdentifiableSource source = media.addSource(OriginalSourceType.PrimaryMediaSource, fact, "Fig.", ref, "p. " + page);
783
    		    source.setOriginalNameString(originalName);
784
    		    media.setArtist(getSalvadorArtist());
785
    		    media.addRights(getSalvadorCopyright(vol));
786
    		    String uriStr = String.format(uriStrFormat, vol, fact);
787
    		    String thumbUriStr = String.format(uriStrFormat, vol, thumbs + fact);
788
    		    uri = new URI(uriStr);
789
    		    thumbUri = new URI(thumbUriStr);
790
    		}else{
791
    		    uri = new URI(fact.trim());
792
    		    thumbUri = null;
793
    		}
794

    
795
    		makeMediaRepresentation(media, uri);
796
    		if (thumbUri != null){
797
                makeMediaRepresentation(media, thumbUri);
798
    		}
799

    
800
    		taxonDescription = taxon.getOrCreateImageGallery(sourceRef == null ? null :sourceRef.getTitleCache());
801
		} catch (URISyntaxException e) {
802
            logger.warn("URISyntaxException. Image could not be imported: " + fact);
803
            return null;
804
        }
805
		return taxonDescription;
806
	}
807

    
808

    
809
	/**
810
     * @param intFact
811
     * @param vol
812
     * @return
813
     */
814
    private String getSalvadorImageTitle(Integer intFact, String vol) {
815
        initSalvadorImagesFile();
816
        String[] line = salvadorImages.get(intFact);
817
        if (line == null){
818
            logger.warn("Could not find salvador image metadata for " + intFact);
819
            return String.valueOf(intFact);
820
        }else{
821
            String name = getSalvadorImageNameInfo(intFact);
822
            String result = UTF8.QUOT_DBL_LEFT +  name + UTF8.QUOT_DBL_RIGHT + " [Berendsohn & al. " + vol + "]";
823
            return result;
824
        }
825
    }
826

    
827

    
828
    private Map<Integer, String[]> salvadorImages = null;
829
    private String getSalvadorImageDescription(Integer intFact) {
830
        initSalvadorImagesFile();
831
        String[] line = salvadorImages.get(intFact);
832
        if (line == null){
833
            logger.warn("Could not find salvador image metadata for " + intFact);
834
            return String.valueOf(intFact);
835
        }else{
836
            int i = 2;
837
            String result = CdmUtils.concat(" " + UTF8.EN_DASH + " ", line[i], line[i + 1]);
838
            return result;
839
        }
840
    }
841

    
842
    private String getSalvadorImageNameInfo(Integer intFact) {
843
        initSalvadorImagesFile();
844
        String[] line = salvadorImages.get(intFact);
845
        if (line == null){
846
            logger.warn("Could not find salvador image metadata for " + intFact);
847
            return String.valueOf(intFact);
848
        }else{
849
            int i = 1;
850
            String result = line[i].substring("Fig. ".length() + line[0].length()).trim();
851
            return result;
852
        }
853
    }
854

    
855
    private void initSalvadorImagesFile() {
856
        if (salvadorImages == null){
857
            salvadorImages = new HashMap<>();
858
            try {
859
                CSVReader reader = new CSVReader(CdmUtils.getUtf8ResourceReader("salvador" + CdmUtils.getFolderSeperator() + "SalvadorImages.csv"),';');
860
                List<String[]> lines = reader.readAll();
861
                for (String[] line : lines){
862
                    String first = line[0];
863
                    if(! "ID".equals(first)){
864
                        try {
865
                            salvadorImages.put(Integer.valueOf(first), line);
866
                        } catch (NumberFormatException e) {
867
                            logger.warn("Number not recognized: " + first);
868
                        }
869
                    }
870
                }
871
                reader.close();
872
            } catch (IOException e) {
873
                e.printStackTrace();
874
            }
875
        }
876
    }
877

    
878

    
879
    private Rights rights1;
880
    private Rights rights2;
881
    private Rights rights3;
882

    
883
    private Rights getSalvadorCopyright(String vol) {
884
        initRights();
885
        if ("2009".equals(vol)){
886
            return rights1;
887
        }else if ("2012".equals(vol)){
888
            return rights1;
889
        }else if ("2016".equals(vol)){
890
            return rights3;
891
        }else{
892
            throw new RuntimeException("Volume not recognized: " + vol);
893
        }
894
    }
895

    
896
    private void initRights(){
897
        if (rights1 == null){
898
            String text = "(c) Jardín Botánico y Museo Botánico Berlin-Dahlem & Asociación Jardín Botánico La Laguna. Berlin, Antiguo Cuscatlán 2009.";
899
            rights1 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
900
            text = "(c) Jardín Botánico y Museo Botánico Berlin-Dahlem & Asociación Jardín Botánico La Laguna. Berlin, Antiguo Cuscatlán 2012.";
901
            rights2 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
902
            text = "(c) Jardín Botánico y Museo Botánico Berlin-Dahlem & Asociación Jardín Botánico La Laguna. Berlin, Antiguo Cuscatlán 2016.";
903
            rights3 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
904
            getCommonService().save(rights1);
905
            getCommonService().save(rights2);
906
            getCommonService().save(rights3);
907
        }
908
    }
909

    
910
    private Integer salvadorArtistId;
911
    private AgentBase<?> getSalvadorArtist() {
912
        if (salvadorArtistId == null){
913
            Person person = Person.NewInstance();
914
            person.setGivenName("José Gerver");
915
            person.setFamilyName("Molina");
916
            salvadorArtistId = getAgentService().save(person).getId();
917
            return person;
918
        }else{
919
            return getAgentService().find(salvadorArtistId);
920
        }
921
    }
922

    
923
    private Integer salvadorRef1Id;
924
    private Integer salvadorRef2Id;
925
    private Integer salvadorRef3Id;
926

    
927
    private Reference getSalvadorReference(String vol){
928
        if (salvadorRef1Id == null){
929
            makeSalvadorReferences();
930
        }
931
        if ("2009".equals(vol)){
932
            return getReferenceService().find(salvadorRef1Id);
933
        }else if ("2012".equals(vol)){
934
            return getReferenceService().find(salvadorRef2Id);
935
        }else if ("2016".equals(vol)){
936
            return getReferenceService().find(salvadorRef3Id);
937
        }else{
938
            throw new RuntimeException("Volume not recognized: " + vol);
939
        }
940

    
941
    }
942

    
943
    private void makeSalvadorReferences() {
944
        Person walter = Person.NewTitledInstance("Berendsohn, W. G.");
945
        walter.setGivenName("Walter G.");
946
        walter.setFamilyName("Berendsohn");
947
        Person katja = Person.NewTitledInstance("Gruber, Anne Kathrina");
948
        katja.setGivenName("Anne Katharina");
949
        katja.setFamilyName("Gruber");
950
        Person monte = Person.NewTitledInstance("Monterrosa Salomón, J.");
951
        Person olmedo = Person.NewTitledInstance("Olmedo Galán, P.");
952
        Person rodriguez = Person.NewTitledInstance("Rodríguez Delcid, D");
953

    
954
        Team team1 = Team.NewInstance();
955
        team1.addTeamMember(walter);
956
        team1.addTeamMember(katja);
957
        team1.addTeamMember(monte);
958

    
959
        Team team2 = Team.NewInstance();
960
        team2.addTeamMember(walter);
961
        team2.addTeamMember(katja);
962
        team2.addTeamMember(rodriguez);
963
        team2.addTeamMember(olmedo);
964

    
965
        Reference vol1 = ReferenceFactory.newBook();
966
        Reference vol2 = ReferenceFactory.newBook();
967
        Reference vol3 = ReferenceFactory.newBook();
968

    
969
        vol1.setAuthorship(team1);
970
        vol2.setAuthorship(team1);
971
        vol3.setAuthorship(team2);
972

    
973
        vol1.setDatePublished(TimePeriodParser.parseStringVerbatim("2009"));
974
        vol2.setDatePublished(TimePeriodParser.parseStringVerbatim("2012"));
975
        vol3.setDatePublished(TimePeriodParser.parseStringVerbatim("2016"));
976

    
977
        Reference englera = ReferenceFactory.newPrintSeries("Englera");
978
        vol1.setInSeries(englera);
979
        vol2.setInSeries(englera);
980
        vol3.setInSeries(englera);
981

    
982
        vol1.setTitle("Nova Silva Cuscatlanica, Árboles nativos e introducidos de El Salvador - Parte 1: Angiospermae - Familias A a L");
983
        vol2.setTitle("Nova Silva Cuscatlanica, Árboles nativos e introducidos de El Salvador - Parte 2: Angiospermae - Familias M a P y Pteridophyta");
984
        vol3.setTitle("Nova Silva Cuscatlanica, Árboles nativos e introducidos de El Salvador - Parte 3: Angiospermae - Familias R a Z y Gymnospermae");
985

    
986
        vol1.setVolume("29(1)");
987
        vol2.setVolume("29(2)");
988
        vol3.setVolume("29(3)");
989

    
990
        vol1.setPages("1-438");
991
        vol2.setVolume("1-300");
992
        vol3.setVolume("1-356");
993

    
994
        String placePublished = "Berlin: Botanic Garden and Botanical Museum Berlin; Antiguo Cuscatlán: Asociación Jardín Botánico La Laguna, El Salvador";
995
        vol1.setPlacePublished(placePublished);
996
        vol2.setPlacePublished(placePublished);
997
        vol3.setPlacePublished(placePublished);
998

    
999
        salvadorRef1Id = getReferenceService().save(vol1).getId();
1000
        salvadorRef2Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol2)).getId();
1001
        salvadorRef3Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol3)).getId();
1002
        return;
1003
    }
1004

    
1005

    
1006
    /**
1007
     * @param media
1008
     * @param uri
1009
     * @param imageInfo
1010
     * @param size
1011
     */
1012
    private void makeMediaRepresentation(Media media, URI uri) {
1013
        ImageInfo imageInfo = null;
1014
        Integer size = null;
1015
        try {
1016
            imageInfo = ImageInfo.NewInstance(uri, 30);
1017
        } catch (IOException | HttpException e) {
1018
            logger.error("Error when reading image meta: " + e + ", "+ uri.toString());
1019
        }
1020
        String mimeType = imageInfo == null ? null : imageInfo.getMimeType();
1021
        String suffix = imageInfo == null ? null : imageInfo.getSuffix();
1022
        MediaRepresentation mediaRepresentation = MediaRepresentation.NewInstance(mimeType, suffix);
1023
        media.addRepresentation(mediaRepresentation);
1024
        ImageFile image = ImageFile.NewInstance(uri, size, imageInfo);
1025
        mediaRepresentation.addRepresentationPart(image);
1026
    }
1027

    
1028
	private TaxonBase<?> getTaxon(Map<String, TaxonBase> taxonMap, Integer taxonIdObj, Number taxonId){
1029
		if (taxonIdObj != null){
1030
			return taxonMap.get(String.valueOf(taxonId));
1031
		}else{
1032
			return null;
1033
		}
1034

    
1035
	}
1036

    
1037
	private Feature getFeature(Map<Integer, Feature>  featureMap, Integer categoryFkInt){
1038
		if (categoryFkInt != null){
1039
			return featureMap.get(categoryFkInt);
1040
		}else{
1041
			return null;
1042
		}
1043

    
1044
	}
1045

    
1046
	@Override
1047
	protected boolean doCheck(BerlinModelImportState state){
1048
		IOValidator<BerlinModelImportState> validator = new BerlinModelFactsImportValidator();
1049
		return validator.validate(state);
1050
	}
1051

    
1052
	@Override
1053
	protected boolean isIgnore(BerlinModelImportState state){
1054
		return ! state.getConfig().isDoFacts();
1055
	}
1056

    
1057

    
1058

    
1059
}
(4-4/21)