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.File;
13
import java.io.IOException;
14
import eu.etaxonomy.cdm.common.URI;
15
import java.net.URISyntaxException;
16
import java.sql.ResultSet;
17
import java.sql.SQLException;
18
import java.util.HashMap;
19
import java.util.HashSet;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.Set;
23
import java.util.UUID;
24

    
25
import javax.validation.constraints.NotNull;
26

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

    
32
import au.com.bytecode.opencsv.CSVReader;
33
import eu.etaxonomy.cdm.common.CdmUtils;
34
import eu.etaxonomy.cdm.common.UTF8;
35
import eu.etaxonomy.cdm.common.media.CdmImageInfo;
36
import eu.etaxonomy.cdm.database.update.DatabaseTypeNotSupportedException;
37
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
38
import eu.etaxonomy.cdm.io.berlinModel.in.validation.BerlinModelFactsImportValidator;
39
import eu.etaxonomy.cdm.io.common.IOValidator;
40
import eu.etaxonomy.cdm.io.common.ResultSetPartitioner;
41
import eu.etaxonomy.cdm.io.common.Source;
42
import eu.etaxonomy.cdm.model.agent.AgentBase;
43
import eu.etaxonomy.cdm.model.agent.Person;
44
import eu.etaxonomy.cdm.model.agent.Team;
45
import eu.etaxonomy.cdm.model.common.Annotation;
46
import eu.etaxonomy.cdm.model.common.AnnotationType;
47
import eu.etaxonomy.cdm.model.common.CdmBase;
48
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
49
import eu.etaxonomy.cdm.model.common.Language;
50
import eu.etaxonomy.cdm.model.common.LanguageString;
51
import eu.etaxonomy.cdm.model.common.Marker;
52
import eu.etaxonomy.cdm.model.common.MarkerType;
53
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
54
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
55
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
56
import eu.etaxonomy.cdm.model.description.Distribution;
57
import eu.etaxonomy.cdm.model.description.Feature;
58
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
59
import eu.etaxonomy.cdm.model.description.TaxonDescription;
60
import eu.etaxonomy.cdm.model.description.TextData;
61
import eu.etaxonomy.cdm.model.location.NamedArea;
62
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
63
import eu.etaxonomy.cdm.model.location.NamedAreaType;
64
import eu.etaxonomy.cdm.model.media.ImageFile;
65
import eu.etaxonomy.cdm.model.media.Media;
66
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
67
import eu.etaxonomy.cdm.model.media.Rights;
68
import eu.etaxonomy.cdm.model.media.RightsType;
69
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
70
import eu.etaxonomy.cdm.model.reference.Reference;
71
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
72
import eu.etaxonomy.cdm.model.taxon.Taxon;
73
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
74
import eu.etaxonomy.cdm.model.term.Representation;
75
import eu.etaxonomy.cdm.model.term.TermBase;
76
import eu.etaxonomy.cdm.model.term.TermTree;
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, Feature.class,
110
	            "Berlin Model Import Feature Vocabulary", "Berlin Model Import Feature Vocabulary", null, null);
111
	    getVocabularyService().save(newVoc);
112

    
113
	    return newVoc;
114
	}
115

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

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

    
122
        TermTree<Feature> featureTree = (!createFeatureTree) ? null : TermTree.NewFeatureInstance(state.getConfig().getFeatureTreeUuid());
123
        if (featureTree!= null && createFeatureTree){
124
            featureTree.setTitleCache(state.getConfig().getFeatureTreeTitle(), true);
125
        }
126

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

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

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

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

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

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

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

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

    
191

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

    
223
    }
224

    
225

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

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

    
243

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

    
254

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

    
265

    
266
    }
267

    
268

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

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

    
291
		return strQuery;
292
	}
293

    
294

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

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

    
323
		ResultSet rs = partitioner.getResultSet();
324

    
325
		Reference sourceRef = state.getTransactionalSourceReference();
326

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

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

    
343
					TaxonBase<?> taxonBase = getTaxon(taxonMap, taxonId, taxonId);
344
					Feature feature = getFeature(featureMap, categoryFkInt) ;
345

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

    
356
						//textData
357
						TextData textData = null;
358
						boolean newTextData = true;
359

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

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

    
392

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

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

    
406
						Language lang = Language.DEFAULT();
407
						if (state.getConfig().isSalvador()){
408
						    lang = getSalvadorFactLanguage(categoryFkInt);
409
						}
410
						if (! taxonDescription.isImageGallery()){
411
							textData.putText(lang, fact);
412
							textData.setFeature(feature);
413
						}
414

    
415
						DescriptionElementBase deb = textData;
416

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

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

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

    
471
						//						if (categoryFkObj == FACT_DESCRIPTION){
472
	//						//;
473
	//					}else if (categoryFkObj == FACT_OBSERVATION){
474
	//						//;
475
	//					}else if (categoryFkObj == FACT_DISTRIBUTION_EM){
476
	//						//
477
	//					}else {
478
	//						//TODO
479
	//						//logger.warn("FactCategory " + categoryFk + " not yet implemented");
480
	//					}
481

    
482
						//notes
483
						doCreatedUpdatedNotes(state, deb, rs);
484
						doId(state, deb, factId, "Fact");
485

    
486
						//TODO
487
						//Designation References -> unclear how to map to CDM
488

    
489

    
490
						//sequence -> textData is not an identifiable entity therefore extensions are not possible
491
						//fact category better
492

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

    
511

    
512
	/**
513
     * @param taxonDescription
514
     * @param salvadorDistribution
515
     */
516
    private void mergeSalvadorDistribution(TaxonDescription taxonDescription,
517
            @NotNull Distribution newDistribution) {
518

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

    
541

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

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

    
601

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

    
615

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

    
625
		TaxonDescription taxonDescription = null;
626
		Set<TaxonDescription> descriptionSet= taxon.getDescriptions();
627

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

    
634
			if (taxonDescription == null){
635
				return null;
636
			}
637

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

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

    
688

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

    
697

    
698
	@Override
699
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
700

    
701
	    String nameSpace;
702
		Set<String> idSet;
703
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<>();
704

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

    
717
			//taxon map
718
			nameSpace = BerlinModelTaxonImport.NAMESPACE;
719
			idSet = taxonIdSet;
720
			@SuppressWarnings("rawtypes")
721
            Map<String, TaxonBase> taxonMap = getCommonService().getSourcedObjectsByIdInSourceC(TaxonBase.class, idSet, nameSpace);
722
			result.put(nameSpace, taxonMap);
723

    
724
			//reference map
725
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
726
			idSet = referenceIdSet;
727
			Map<String, Reference> referenceMap = getCommonService().getSourcedObjectsByIdInSourceC(Reference.class, idSet, nameSpace);
728
			result.put(nameSpace, referenceMap);
729

    
730
			//refDetail map
731
			nameSpace = BerlinModelRefDetailImport.REFDETAIL_NAMESPACE;
732
			idSet = refDetailIdSet;
733
			Map<String, Reference> refDetailMap= getCommonService().getSourcedObjectsByIdInSourceC(Reference.class, idSet, nameSpace);
734
			result.put(nameSpace, refDetailMap);
735

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

    
742

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

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

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

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

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

    
804

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

    
823

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

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

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

    
874

    
875
    private Rights rights1;
876
    private Rights rights2;
877
    private Rights rights3;
878

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

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

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

    
919
    private Integer salvadorRef1Id;
920
    private Integer salvadorRef2Id;
921
    private Integer salvadorRef3Id;
922

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

    
937
    }
938

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

    
950
        Team team1 = Team.NewInstance();
951
        team1.addTeamMember(walter);
952
        team1.addTeamMember(katja);
953
        team1.addTeamMember(monte);
954

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

    
961
        Reference vol1 = ReferenceFactory.newBook();
962
        Reference vol2 = ReferenceFactory.newBook();
963
        Reference vol3 = ReferenceFactory.newBook();
964

    
965
        vol1.setAuthorship(team1);
966
        vol2.setAuthorship(team1);
967
        vol3.setAuthorship(team2);
968

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

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

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

    
982
        vol1.setVolume("29(1)");
983
        vol2.setVolume("29(2)");
984
        vol3.setVolume("29(3)");
985

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

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

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

    
1001

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

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

    
1031
	}
1032

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

    
1040
	}
1041

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

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

    
1053

    
1054

    
1055
}
(5-5/22)