Project

General

Profile

Download (41.1 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.description.CommonTaxonName;
53
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
54
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
55
import eu.etaxonomy.cdm.model.description.Distribution;
56
import eu.etaxonomy.cdm.model.description.Feature;
57
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
58
import eu.etaxonomy.cdm.model.description.TaxonDescription;
59
import eu.etaxonomy.cdm.model.description.TextData;
60
import eu.etaxonomy.cdm.model.location.NamedArea;
61
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
62
import eu.etaxonomy.cdm.model.location.NamedAreaType;
63
import eu.etaxonomy.cdm.model.media.ImageFile;
64
import eu.etaxonomy.cdm.model.media.Media;
65
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
66
import eu.etaxonomy.cdm.model.media.Rights;
67
import eu.etaxonomy.cdm.model.media.RightsType;
68
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
69
import eu.etaxonomy.cdm.model.reference.Reference;
70
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
71
import eu.etaxonomy.cdm.model.taxon.Taxon;
72
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
73
import eu.etaxonomy.cdm.model.term.FeatureNode;
74
import eu.etaxonomy.cdm.model.term.FeatureTree;
75
import eu.etaxonomy.cdm.model.term.Representation;
76
import eu.etaxonomy.cdm.model.term.TermBase;
77
import eu.etaxonomy.cdm.model.term.TermType;
78
import eu.etaxonomy.cdm.model.term.TermVocabulary;
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

    
115
	private Map<Integer, Feature>  invokeFactCategories(BerlinModelImportState state){
116

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

    
121
        FeatureTree featureTree = (!createFeatureTree) ? null : FeatureTree.NewInstance(state.getConfig().getFeatureTreeUuid());
122
        if (featureTree!= null && createFeatureTree){
123
            featureTree.setTitleCache(state.getConfig().getFeatureTreeTitle(), true);
124
        }
125

    
126
        try {
127
			//get data from database
128
			String strQuery =
129
					" SELECT FactCategory.* " +
130
					" FROM FactCategory "+
131
                    " WHERE (1=1)";
132
			if (state.getConfig().isSalvador()){
133
			    strQuery += " AND " + state.getConfig().getFactFilter().replace("factCategoryFk", "factCategoryId");
134
			}
135

    
136
			ResultSet rs = source.getResultSet(strQuery) ;
137

    
138
			TermVocabulary<Feature> featureVocabulary = getFeatureVocabulary();
139
			int i = 0;
140
			//for each reference
141
			while (rs.next()){
142

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

    
145
				int factCategoryId = rs.getInt("factCategoryId");
146
				String factCategory = rs.getString("factCategory");
147

    
148
				Feature feature;
149
				try {
150
					feature = BerlinModelTransformer.factCategory2Feature(factCategoryId);
151
				} catch (UnknownCdmTypeException e) {
152
					UUID featureUuid = null;
153
					featureUuid = BerlinModelTransformer.getFeatureUuid(String.valueOf(factCategoryId+"-"+factCategory));
154
					if (featureUuid == null){
155
						logger.warn("New Feature (FactCategoryId: " + factCategoryId + ")");
156
						featureUuid = UUID.randomUUID();
157
					}
158
					feature = getFeature(state, featureUuid, factCategory, factCategory, null, featureVocabulary);
159
					if (state.getConfig().isSalvador()){
160
					    adaptNewSalvadorFeature(factCategoryId, feature);
161
					}
162
                    //id
163
                    doId(state, feature, factCategoryId, "FactCategory");
164

    
165
					//TODO
166
//					MaxFactNumber	int	Checked
167
//					ExtensionTableName	varchar(100)	Checked
168
//					Description	nvarchar(1000)	Checked
169
//					locExtensionFormName	nvarchar(80)	Checked
170
//					RankRestrictionFk	int	Checked
171
				}
172

    
173
				result.put(factCategoryId, feature);
174
				if (createFeatureTree && isPublicFeature(factCategoryId)){
175
				    featureTree.getRoot().addChild(FeatureNode.NewInstance(feature));
176
				}
177
			}
178
			if (createFeatureTree){
179
			    featureTree.getRoot().addChild(FeatureNode.NewInstance(Feature.DISTRIBUTION()),2);
180
                featureTree.getRoot().addChild(FeatureNode.NewInstance(Feature.NOTES()), featureTree.getRoot().getChildCount()-1);
181
			    getFeatureTreeService().save(featureTree);
182
			}
183
			return result;
184
		} catch (SQLException e) {
185
			logger.error("SQLException:" +  e);
186
			return null;
187
		}
188
	}
189

    
190

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

    
222
    }
223

    
224

    
225
    /**
226
     * @param feature
227
     * @param string
228
     */
229
    private void addSpanishRepresentationLabel(TermBase term, String label) {
230
        term.getRepresentations().add(Representation.NewInstance(label, label, null, Language.SPANISH_CASTILIAN()));
231
    }
232

    
233
    /**
234
     * @param feature
235
     * @param string
236
     */
237
    private void addEnglishFactCategoryName(Feature feature, String label) {
238
        feature.getRepresentations().iterator().next().setLanguage(Language.SPANISH_CASTILIAN());
239
        feature.getRepresentations().add(Representation.NewInstance(label, label, null, Language.ENGLISH()));
240
    }
241

    
242

    
243
    @Override
244
	protected void doInvoke(BerlinModelImportState state) {
245
        if (state.getConfig().isSalvador()){
246
            invokeSpanishTermLabels();
247
        }
248
		featureMap = invokeFactCategories(state);
249
		super.doInvoke(state);
250
		return;
251
	}
252

    
253

    
254
	/**
255
     *
256
     */
257
    private void invokeSpanishTermLabels() {
258
        addSpanishRepresentationLabel(Feature.NOTES(), "Notas");
259
        addSpanishRepresentationLabel(NamedAreaLevel.DEPARTMENT(), "Departamento");
260
        addSpanishRepresentationLabel(PresenceAbsenceTerm.NATIVE(), "nativo");
261
        addSpanishRepresentationLabel(PresenceAbsenceTerm.CULTIVATED(), "cultivado");
262
        addSpanishRepresentationLabel(PresenceAbsenceTerm.PRESENT(), "presente");
263

    
264

    
265
    }
266

    
267

    
268
    @Override
269
	protected String getIdQuery(BerlinModelImportState state) {
270
		String result = super.getIdQuery(state);
271
		if (StringUtils.isNotBlank(state.getConfig().getFactFilter())){
272
			result += " WHERE " + state.getConfig().getFactFilter();
273
		}else{
274
			result = super.getIdQuery(state);
275
		}
276
		result += getOrderBy(state.getConfig());
277
		return result;
278
	}
279

    
280
	@Override
281
	protected String getRecordQuery(BerlinModelImportConfigurator config) {
282
			String strQuery =
283
					" SELECT Fact.*, PTaxon.RIdentifier as taxonId, RefDetail.Details " +
284
					" FROM Fact " +
285
                      	" INNER JOIN PTaxon ON Fact.PTNameFk = PTaxon.PTNameFk AND Fact.PTRefFk = PTaxon.PTRefFk " +
286
                      	" LEFT OUTER JOIN RefDetail ON Fact.FactRefDetailFk = RefDetail.RefDetailId AND Fact.FactRefFk = RefDetail.RefFk " +
287
              	" WHERE (FactId IN (" + ID_LIST_TOKEN + "))";
288
			    strQuery += getOrderBy(config);
289

    
290
		return strQuery;
291
	}
292

    
293

    
294
	private String getOrderBy(BerlinModelImportConfigurator config) {
295
		String result;
296
		try{
297
			if (config.getSource().checkColumnExists("Fact", "Sequence")){
298
				result = " ORDER By Fact.Sequence, Fact.FactId";
299
				if (config.isSalvador()){
300
				    result = " ORDER By Fact.FactCategoryFk, Fact.Sequence, Fact.FactId";
301
				}
302
			}else{
303
				result = " ORDER By Fact.FactId";
304
			}
305
		} catch (DatabaseTypeNotSupportedException e) {
306
			logger.info("checkColumnExists not supported");
307
			result = " ORDER By Fact.FactId";
308
		}
309
		return result;
310
	}
311

    
312
	@Override
313
	public boolean doPartition(@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner, BerlinModelImportState state) {
314
		boolean success = true ;
315
		Set<TaxonBase> taxaToSave = new HashSet<>();
316
		Map<String, TaxonBase> taxonMap = partitioner.getObjectMap(BerlinModelTaxonImport.NAMESPACE);
317
		Map<String, Reference> refMap = partitioner.getObjectMap(BerlinModelReferenceImport.REFERENCE_NAMESPACE);
318

    
319
		ResultSet rs = partitioner.getResultSet();
320

    
321
		Reference sourceRef = state.getTransactionalSourceReference();
322

    
323
		try{
324
			int i = 0;
325
			//for each fact
326
			while (rs.next()){
327
				try{
328
					if ((i++ % modCount) == 0){ logger.info("Facts handled: " + (i-1));}
329

    
330
					int factId = rs.getInt("factId");
331
					Integer taxonId = nullSafeInt(rs, "taxonId");
332
					Integer factRefFkInt = nullSafeInt(rs, "factRefFk");
333
					Integer categoryFkInt = nullSafeInt(rs, "factCategoryFk");
334
					String details = rs.getString("Details");
335
					String fact = CdmUtils.Nz(rs.getString("Fact"));
336
					String notes = CdmUtils.Nz(rs.getString("notes"));
337
					Boolean doubtfulFlag = rs.getBoolean("DoubtfulFlag");
338

    
339
					TaxonBase<?> taxonBase = getTaxon(taxonMap, taxonId, taxonId);
340
					Feature feature = getFeature(featureMap, categoryFkInt) ;
341

    
342
					if (taxonBase == null){
343
						logger.warn("Taxon for Fact " + factId + " does not exist in store");
344
						success = false;
345
					}else{
346
						TaxonDescription taxonDescription;
347
						if ( (taxonDescription = getMyTaxonDescripion(taxonBase, state, categoryFkInt, taxonId, factId, fact, sourceRef)) == null){
348
							success = false;
349
							continue;
350
						}
351

    
352
						//textData
353
						TextData textData = null;
354
						boolean newTextData = true;
355

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

    
378
						if (taxonDescription.isImageGallery()){
379
						    newTextData = false;
380
						    textData = (TextData)taxonDescription.getElements().iterator().next();
381
						}
382
						if(newTextData == true)	{
383
							textData = TextData.NewInstance();
384
						}else if (textData == null){
385
						    throw new RuntimeException("Textdata must not be null");  //does not happen, only to avoid potential NPE warnings
386
						}
387

    
388

    
389
						//for diptera database
390
						if (categoryFkInt.equals(99) && notes.contains("<OriginalName>")){
391
							fact = notes + ": " +  fact ;
392
						}
393
						//for E+M maps
394
						if (categoryFkInt.equals(14) && state.getConfig().isRemoveHttpMapsAnchor() && fact.contains("<a href")){
395
							//example <a href="http://euromed.luomus.fi/euromed_map.php?taxon=280629&size=medium">distribution</a>
396
							fact = fact.replace("<a href=\"", "").replace("\">distribution</a>", "");
397
						}
398

    
399
						//TODO textData.putText(fact, bmiConfig.getFactLanguage());  //doesn't work because  bmiConfig.getFactLanguage() is not not a persistent Language Object
400
						//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
401

    
402
						Language lang = Language.DEFAULT();
403
						if (state.getConfig().isSalvador()){
404
						    lang = getSalvadorFactLanguage(categoryFkInt);
405
						}
406
						if (! taxonDescription.isImageGallery()){
407
							textData.putText(lang, fact);
408
							textData.setFeature(feature);
409
						}
410

    
411
						DescriptionElementBase deb = textData;
412

    
413
						if (state.getConfig().isSalvador()){
414
						    if (categoryFkInt == 306){
415
						        NamedArea area = null;  // for now we do not set an area as it can not be disabled in dataportals via css yet
416
						        deb = CommonTaxonName.NewInstance(fact, Language.SPANISH_CASTILIAN(), area);
417
						    }else if (categoryFkInt == 307){
418
						        Distribution salvadorDistribution = salvadorDistributionFromMuestrasDeHerbar((Taxon)taxonBase, fact);
419
						        if (salvadorDistribution != null){
420
						            //id
421
						            doId(state, salvadorDistribution, factId, "Fact");
422
						            mergeSalvadorDistribution(taxonDescription, salvadorDistribution);
423
						        }
424
						    }
425
						}
426

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

    
456
						//Sequence
457
						Integer sequence = rs.getInt("Sequence");
458
						if (sequence != 999){
459
							String strSequence = String.valueOf(sequence);
460
							strSequence = SEQUENCE_PREFIX + strSequence;
461
							//TODO make it an Extension when possible
462
							//Extension datesExtension = Extension.NewInstance(textData, strSequence, ExtensionType.ORDER());
463
							Annotation annotation = Annotation.NewInstance(strSequence, AnnotationType.TECHNICAL(), Language.ENGLISH());
464
							deb.addAnnotation(annotation);
465
						}
466

    
467
						//						if (categoryFkObj == FACT_DESCRIPTION){
468
	//						//;
469
	//					}else if (categoryFkObj == FACT_OBSERVATION){
470
	//						//;
471
	//					}else if (categoryFkObj == FACT_DISTRIBUTION_EM){
472
	//						//
473
	//					}else {
474
	//						//TODO
475
	//						//logger.warn("FactCategory " + categoryFk + " not yet implemented");
476
	//					}
477

    
478
						//notes
479
						doCreatedUpdatedNotes(state, deb, rs);
480
						doId(state, deb, factId, "Fact");
481

    
482
						//TODO
483
						//Designation References -> unclear how to map to CDM
484

    
485

    
486
						//sequence -> textData is not an identifiable entity therefore extensions are not possible
487
						//fact category better
488

    
489
						taxaToSave.add(taxonBase);
490
					}
491
				} catch (Exception re){
492
					logger.error("An exception occurred during the facts import");
493
					re.printStackTrace();
494
					success = false;
495
				}
496
				//put
497
			}
498
			logger.info("Facts handled: " + (i-1));
499
			logger.info("Taxa to save: " + taxaToSave.size());
500
			getTaxonService().save(taxaToSave);
501
		}catch(SQLException e){
502
			throw new RuntimeException(e);
503
		}
504
		return success;
505
	}
506

    
507

    
508
	/**
509
     * @param taxonDescription
510
     * @param salvadorDistribution
511
     */
512
    private void mergeSalvadorDistribution(TaxonDescription taxonDescription,
513
            @NotNull Distribution newDistribution) {
514

    
515
        Distribution existingDistribution = null;
516
        for (DescriptionElementBase deb : taxonDescription.getElements()){
517
            if (deb.isInstanceOf(Distribution.class)){
518
                Distribution distribution = CdmBase.deproxy(deb, Distribution.class);
519
                if (distribution.getArea() != null && distribution.getArea().equals(newDistribution.getArea())){
520
                    existingDistribution = distribution;
521
                    break;
522
                }else if (distribution.getArea() == null){
523
                    logger.warn("Area for distribution is null: " + distribution.getUuid());
524
                }
525
            }
526
        }
527
        if (existingDistribution == null){
528
            taxonDescription.addElement(newDistribution);
529
        }else if(!existingDistribution.getStatus().equals(newDistribution.getStatus())){
530
            //should not happen
531
            logger.warn("Taxon has areas with different distribution states: " + taxonDescription.getTaxon().getTitleCache());
532
        }else{
533
            //do nothing, distribution already exists
534
        }
535
    }
536

    
537

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

    
576
    private PresenceAbsenceTerm getSalvadorDistributionState(Taxon taxon) {
577
        boolean hasGlobalDist = false;
578
        for (TaxonDescription desc : taxon.getDescriptions()){
579
            for (DescriptionElementBase deb : desc.getElements()){
580
                if (deb.getFeature().getUuid().equals(BerlinModelTransformer.uuidFeatureDistributionGlobal)){
581
                    hasGlobalDist = true;
582
                    TextData textData = CdmBase.deproxy(deb, TextData.class);
583
                    for (LanguageString text : textData.getMultilanguageText().values()){
584
                        if (text.getText().contains("El Salvador")){
585
                            return PresenceAbsenceTerm.NATIVE();
586
                        }
587
                    }
588
                }
589
            }
590
        }
591
        if (!hasGlobalDist){
592
            logger.warn("No global distribution found: " + taxon.getTitleCache());
593
        }
594
        return hasGlobalDist ? PresenceAbsenceTerm.CULTIVATED(): PresenceAbsenceTerm.PRESENT();
595
    }
596

    
597

    
598
    /**
599
     * @param factId
600
     * @return
601
     */
602
    private Language getSalvadorFactLanguage(int categoryFkInt) {
603
        if (categoryFkInt == 350){
604
            return Language.ENGLISH();
605
        }else if (categoryFkInt == 1800 || categoryFkInt == 1900){
606
            return Language.UNDETERMINED();
607
        }
608
        return Language.SPANISH_CASTILIAN();
609
    }
610

    
611

    
612
    private TaxonDescription getMyTaxonDescripion(TaxonBase taxonBase, BerlinModelImportState state, Integer categoryFk, Integer taxonId, int factId, String fact, Reference sourceRef) {
613
		Taxon taxon = null;
614
		if ( taxonBase instanceof Taxon ) {
615
			taxon = (Taxon) taxonBase;
616
		}else{
617
			logger.warn("TaxonBase " + (taxonId==null?"(null)":taxonId) + " for Fact " + factId + " was not of type Taxon but: " + taxonBase.getClass().getSimpleName());
618
			return null;
619
		}
620

    
621
		TaxonDescription taxonDescription = null;
622
		Set<TaxonDescription> descriptionSet= taxon.getDescriptions();
623

    
624
		Media media = null;
625
		//for diptera / salvador images
626
		if (categoryFk == 51 || categoryFk == 312){  //TODO check also FactCategory string
627
			media = Media.NewInstance();
628
			taxonDescription = makeImage(state, fact, media, descriptionSet, taxon);
629

    
630
			if (taxonDescription == null){
631
				return null;
632
			}
633

    
634
			TextData textData = null;
635
			for (DescriptionElementBase el:  taxonDescription.getElements()){
636
				if (el.isInstanceOf(TextData.class)){
637
					textData = CdmBase.deproxy(el, TextData.class);
638
				}
639
			}
640
			if (textData == null){
641
				textData = TextData.NewInstance(Feature.IMAGE());
642
				taxonDescription.addElement(textData);
643
			}
644
			textData.addMedia(media);
645
		}
646
		//all others (no image) -> getDescription
647
		else{
648
			boolean isPublic = isPublicFeature(categoryFk);
649
		    for (TaxonDescription desc: descriptionSet){
650

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

    
684

    
685
    /**
686
     * @param categoryFk
687
     * @return
688
     */
689
    private boolean isPublicFeature(Integer categoryFk) {
690
        return ! (categoryFk == 1800 || categoryFk == 1900 || categoryFk == 2000);
691
    }
692

    
693

    
694
	@Override
695
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
696
		String nameSpace;
697
		Class<?> cdmClass;
698
		Set<String> idSet;
699
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
700

    
701
		try{
702
			Set<String> taxonIdSet = new HashSet<String>();
703
			Set<String> referenceIdSet = new HashSet<String>();
704
			Set<String> refDetailIdSet = new HashSet<String>();
705
			while (rs.next()){
706
				handleForeignKey(rs, taxonIdSet, "taxonId");
707
				handleForeignKey(rs, referenceIdSet, "FactRefFk");
708
				handleForeignKey(rs, referenceIdSet, "PTDesignationRefFk");
709
				handleForeignKey(rs, refDetailIdSet, "FactRefDetailFk");
710
				handleForeignKey(rs, refDetailIdSet, "PTDesignationRefDetailFk");
711
		}
712

    
713
			//taxon map
714
			nameSpace = BerlinModelTaxonImport.NAMESPACE;
715
			cdmClass = TaxonBase.class;
716
			idSet = taxonIdSet;
717
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
718
			result.put(nameSpace, taxonMap);
719

    
720
			//reference map
721
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
722
			cdmClass = Reference.class;
723
			idSet = referenceIdSet;
724
			Map<String, Reference> referenceMap = (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
725
			result.put(nameSpace, referenceMap);
726

    
727
			//refDetail map
728
			nameSpace = BerlinModelRefDetailImport.REFDETAIL_NAMESPACE;
729
			cdmClass = Reference.class;
730
			idSet = refDetailIdSet;
731
			Map<String, Reference> refDetailMap= (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
732
			result.put(nameSpace, refDetailMap);
733

    
734
		} catch (SQLException e) {
735
			throw new RuntimeException(e);
736
	}
737
		return result;
738
	}
739

    
740

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

    
769
                String title = getSalvadorImageTitle(intFact, vol);
770
                media.putTitle(Language.LATIN(), title);
771
                String description = getSalvadorImageDescription(intFact);
772
                media.putDescription(Language.SPANISH_CASTILIAN(), description);
773

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

    
789
    		makeMediaRepresentation(media, uri);
790
    		if (thumbUri != null){
791
                makeMediaRepresentation(media, thumbUri);
792
    		}
793

    
794
    		taxonDescription = taxon.getOrCreateImageGallery(sourceRef == null ? null :sourceRef.getTitleCache());
795
		} catch (URISyntaxException e) {
796
            logger.warn("URISyntaxException. Image could not be imported: " + fact);
797
            return null;
798
        }
799
		return taxonDescription;
800
	}
801

    
802

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

    
821

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

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

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

    
872

    
873
    private Rights rights1;
874
    private Rights rights2;
875
    private Rights rights3;
876

    
877
    private Rights getSalvadorCopyright(String vol) {
878
        initRights();
879
        if ("2009".equals(vol)){
880
            return rights1;
881
        }else if ("2012".equals(vol)){
882
            return rights1;
883
        }else if ("2016".equals(vol)){
884
            return rights3;
885
        }else{
886
            throw new RuntimeException("Volume not recognized: " + vol);
887
        }
888
    }
889

    
890
    private void initRights(){
891
        if (rights1 == null){
892
            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.";
893
            rights1 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
894
            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.";
895
            rights2 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
896
            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.";
897
            rights3 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
898
            getCommonService().save(rights1);
899
            getCommonService().save(rights2);
900
            getCommonService().save(rights3);
901
        }
902
    }
903

    
904
    private Integer salvadorArtistId;
905
    private AgentBase<?> getSalvadorArtist() {
906
        if (salvadorArtistId == null){
907
            Person person = Person.NewInstance();
908
            person.setGivenName("José Gerver");
909
            person.setFamilyName("Molina");
910
            salvadorArtistId = getAgentService().save(person).getId();
911
            return person;
912
        }else{
913
            return getAgentService().find(salvadorArtistId);
914
        }
915
    }
916

    
917
    private Integer salvadorRef1Id;
918
    private Integer salvadorRef2Id;
919
    private Integer salvadorRef3Id;
920

    
921
    private Reference getSalvadorReference(String vol){
922
        if (salvadorRef1Id == null){
923
            makeSalvadorReferences();
924
        }
925
        if ("2009".equals(vol)){
926
            return getReferenceService().find(salvadorRef1Id);
927
        }else if ("2012".equals(vol)){
928
            return getReferenceService().find(salvadorRef2Id);
929
        }else if ("2016".equals(vol)){
930
            return getReferenceService().find(salvadorRef3Id);
931
        }else{
932
            throw new RuntimeException("Volume not recognized: " + vol);
933
        }
934

    
935
    }
936

    
937
    private void makeSalvadorReferences() {
938
        Person walter = Person.NewTitledInstance("Berendsohn, W. G.");
939
        walter.setGivenName("Walter G.");
940
        walter.setFamilyName("Berendsohn");
941
        Person katja = Person.NewTitledInstance("Gruber, Anne Kathrina");
942
        katja.setGivenName("Anne Katharina");
943
        katja.setFamilyName("Gruber");
944
        Person monte = Person.NewTitledInstance("Monterrosa Salomón, J.");
945
        Person olmedo = Person.NewTitledInstance("Olmedo Galán, P.");
946
        Person rodriguez = Person.NewTitledInstance("Rodríguez Delcid, D");
947

    
948
        Team team1 = Team.NewInstance();
949
        team1.addTeamMember(walter);
950
        team1.addTeamMember(katja);
951
        team1.addTeamMember(monte);
952

    
953
        Team team2 = Team.NewInstance();
954
        team2.addTeamMember(walter);
955
        team2.addTeamMember(katja);
956
        team2.addTeamMember(rodriguez);
957
        team2.addTeamMember(olmedo);
958

    
959
        Reference vol1 = ReferenceFactory.newBook();
960
        Reference vol2 = ReferenceFactory.newBook();
961
        Reference vol3 = ReferenceFactory.newBook();
962

    
963
        vol1.setAuthorship(team1);
964
        vol2.setAuthorship(team1);
965
        vol3.setAuthorship(team2);
966

    
967
        vol1.setDatePublished(TimePeriodParser.parseStringVerbatim("2009"));
968
        vol2.setDatePublished(TimePeriodParser.parseStringVerbatim("2012"));
969
        vol3.setDatePublished(TimePeriodParser.parseStringVerbatim("2016"));
970

    
971
        Reference englera = ReferenceFactory.newPrintSeries("Englera");
972
        vol1.setInSeries(englera);
973
        vol2.setInSeries(englera);
974
        vol3.setInSeries(englera);
975

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

    
980
        vol1.setVolume("29(1)");
981
        vol2.setVolume("29(2)");
982
        vol3.setVolume("29(3)");
983

    
984
        vol1.setPages("1-438");
985
        vol2.setVolume("1-300");
986
        vol3.setVolume("1-356");
987

    
988
        String placePublished = "Berlin: Botanic Garden and Botanical Museum Berlin; Antiguo Cuscatlán: Asociación Jardín Botánico La Laguna, El Salvador";
989
        vol1.setPlacePublished(placePublished);
990
        vol2.setPlacePublished(placePublished);
991
        vol3.setPlacePublished(placePublished);
992

    
993
        salvadorRef1Id = getReferenceService().save(vol1).getId();
994
        salvadorRef2Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol2)).getId();
995
        salvadorRef3Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol3)).getId();
996
        return;
997
    }
998

    
999

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

    
1022
	private TaxonBase<?> getTaxon(Map<String, TaxonBase> taxonMap, Integer taxonIdObj, Number taxonId){
1023
		if (taxonIdObj != null){
1024
			return taxonMap.get(String.valueOf(taxonId));
1025
		}else{
1026
			return null;
1027
		}
1028

    
1029
	}
1030

    
1031
	private Feature getFeature(Map<Integer, Feature>  featureMap, Integer categoryFkInt){
1032
		if (categoryFkInt != null){
1033
			return featureMap.get(categoryFkInt);
1034
		}else{
1035
			return null;
1036
		}
1037

    
1038
	}
1039

    
1040
	@Override
1041
	protected boolean doCheck(BerlinModelImportState state){
1042
		IOValidator<BerlinModelImportState> validator = new BerlinModelFactsImportValidator();
1043
		return validator.validate(state);
1044
	}
1045

    
1046
	@Override
1047
	protected boolean isIgnore(BerlinModelImportState state){
1048
		return ! state.getConfig().isDoFacts();
1049
	}
1050

    
1051

    
1052

    
1053
}
(5-5/22)