Project

General

Profile

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

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

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

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

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

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

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

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

    
106

    
107
	private TermVocabulary<Feature> getFeatureVocabulary(){
108
	    TermVocabulary<Feature> newVoc = TermVocabulary.NewInstance(TermType.Feature, "User Defined Feature Vocabulary", "User Defined Feature Vocabulary", null, null);
109
	    getVocabularyService().save(newVoc);
110

    
111
	    return newVoc;
112

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

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

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

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

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

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

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

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

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

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

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

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

    
199

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

    
231
    }
232

    
233

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

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

    
251

    
252
    @Override
253
	protected void doInvoke(BerlinModelImportState state) {
254
		featureMap = invokeFactCategories(state);
255
		super.doInvoke(state);
256
		return;
257
	}
258

    
259

    
260
	@Override
261
	protected String getIdQuery(BerlinModelImportState state) {
262
		String result = super.getIdQuery(state);
263
		if (StringUtils.isNotBlank(state.getConfig().getFactFilter())){
264
			result += " WHERE " + state.getConfig().getFactFilter();
265
		}else{
266
			result = super.getIdQuery(state);
267
		}
268
		result += getOrderBy(state.getConfig());
269
		return result;
270
	}
271

    
272
	@Override
273
	protected String getRecordQuery(BerlinModelImportConfigurator config) {
274
			String strQuery =
275
					" SELECT Fact.*, PTaxon.RIdentifier as taxonId, RefDetail.Details " +
276
					" FROM Fact " +
277
                      	" INNER JOIN PTaxon ON Fact.PTNameFk = PTaxon.PTNameFk AND Fact.PTRefFk = PTaxon.PTRefFk " +
278
                      	" LEFT OUTER JOIN RefDetail ON Fact.FactRefDetailFk = RefDetail.RefDetailId AND Fact.FactRefFk = RefDetail.RefFk " +
279
              	" WHERE (FactId IN (" + ID_LIST_TOKEN + "))";
280
			    strQuery += getOrderBy(config);
281

    
282
		return strQuery;
283
	}
284

    
285

    
286
	private String getOrderBy(BerlinModelImportConfigurator config) {
287
		String result;
288
		try{
289
			if (config.getSource().checkColumnExists("Fact", "Sequence")){
290
				result = " ORDER By Fact.Sequence, Fact.FactId";
291
				if (config.isSalvador()){
292
				    result = " ORDER By Fact.FactCategoryFk, Fact.Sequence, Fact.FactId";
293
				}
294
			}else{
295
				result = " ORDER By Fact.FactId";
296
			}
297
		} catch (DatabaseTypeNotSupportedException e) {
298
			logger.info("checkColumnExists not supported");
299
			result = " ORDER By Fact.FactId";
300
		}
301
		return result;
302
	}
303

    
304
	@Override
305
	public boolean doPartition(ResultSetPartitioner partitioner, BerlinModelImportState state) {
306
		boolean success = true ;
307
		BerlinModelImportConfigurator config = state.getConfig();
308
		Set<TaxonBase> taxaToSave = new HashSet<TaxonBase>();
309
		Map<String, TaxonBase> taxonMap = partitioner.getObjectMap(BerlinModelTaxonImport.NAMESPACE);
310
		Map<String, Reference> refMap = partitioner.getObjectMap(BerlinModelReferenceImport.REFERENCE_NAMESPACE);
311

    
312
		ResultSet rs = partitioner.getResultSet();
313

    
314
		Reference sourceRef = state.getTransactionalSourceReference();
315

    
316
		try{
317
			int i = 0;
318
			//for each fact
319
			while (rs.next()){
320
				try{
321
					if ((i++ % modCount) == 0){ logger.info("Facts handled: " + (i-1));}
322

    
323
					int factId = rs.getInt("factId");
324
					Integer taxonId = nullSafeInt(rs, "taxonId");
325
					Integer factRefFkInt = nullSafeInt(rs, "factRefFk");
326
					Integer categoryFkInt = nullSafeInt(rs, "factCategoryFk");
327
					String details = rs.getString("Details");
328
					String fact = CdmUtils.Nz(rs.getString("Fact"));
329
					String notes = CdmUtils.Nz(rs.getString("notes"));
330
					Boolean doubtfulFlag = rs.getBoolean("DoubtfulFlag");
331

    
332
					TaxonBase<?> taxonBase = getTaxon(taxonMap, taxonId, taxonId);
333
					Feature feature = getFeature(featureMap, categoryFkInt) ;
334

    
335
					if (taxonBase == null){
336
						logger.warn("Taxon for Fact " + factId + " does not exist in store");
337
						success = false;
338
					}else{
339
						TaxonDescription taxonDescription;
340
						if ( (taxonDescription = getMyTaxonDescripion(taxonBase, state, categoryFkInt, taxonId, factId, fact, sourceRef)) == null){
341
							success = false;
342
							continue;
343
						}
344

    
345
						//textData
346
						TextData textData = null;
347
						boolean newTextData = true;
348

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

    
371
						if (taxonDescription.isImageGallery()){
372
						    newTextData = false;
373
						    textData = (TextData)taxonDescription.getElements().iterator().next();
374
						}
375
						if(newTextData == true)	{
376
							textData = TextData.NewInstance();
377
						}
378

    
379

    
380
						//for diptera database
381
						if (categoryFkInt.equals(99) && notes.contains("<OriginalName>")){
382
							fact = notes + ": " +  fact ;
383
						}
384
						//for E+M maps
385
						if (categoryFkInt.equals(14) && state.getConfig().isRemoveHttpMapsAnchor() && fact.contains("<a href")){
386
							//example <a href="http://euromed.luomus.fi/euromed_map.php?taxon=280629&size=medium">distribution</a>
387
							fact = fact.replace("<a href=\"", "").replace("\">distribution</a>", "");
388
						}
389

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

    
393
						Language lang = Language.DEFAULT();
394
						if (state.getConfig().isSalvador()){
395
						    lang = getSalvadorFactLanguage(categoryFkInt);
396
						}
397
						if (! taxonDescription.isImageGallery()){
398
							textData.putText(lang, fact);
399
							textData.setFeature(feature);
400
						}
401

    
402
						DescriptionElementBase deb = textData;
403

    
404
						if (state.getConfig().isSalvador()){
405
						    if (categoryFkInt == 306){
406
						        NamedArea area = null;  // for now we do not set an area as it can not be disabled in dataportals via css yet
407
						        deb = CommonTaxonName.NewInstance(fact, Language.SPANISH_CASTILIAN(), area);
408
						    }else if (categoryFkInt == 307){
409
						        Distribution salvadorDistribution = salvadorDistributionFromMuestrasDeHerbar((Taxon)taxonBase, fact);
410
						        if (salvadorDistribution != null){
411
						            //id
412
						            doId(state, salvadorDistribution, factId, "Fact");
413
						            mergeSalvadorDistribution(taxonDescription, salvadorDistribution);
414
						        }
415
						    }
416
						}
417

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

    
447
						//Sequence
448
						Integer sequence = rs.getInt("Sequence");
449
						if (sequence != 999){
450
							String strSequence = String.valueOf(sequence);
451
							strSequence = SEQUENCE_PREFIX + strSequence;
452
							//TODO make it an Extension when possible
453
							//Extension datesExtension = Extension.NewInstance(textData, strSequence, ExtensionType.ORDER());
454
							Annotation annotation = Annotation.NewInstance(strSequence, AnnotationType.TECHNICAL(), Language.ENGLISH());
455
							deb.addAnnotation(annotation);
456
						}
457

    
458
						//						if (categoryFkObj == FACT_DESCRIPTION){
459
	//						//;
460
	//					}else if (categoryFkObj == FACT_OBSERVATION){
461
	//						//;
462
	//					}else if (categoryFkObj == FACT_DISTRIBUTION_EM){
463
	//						//
464
	//					}else {
465
	//						//TODO
466
	//						//logger.warn("FactCategory " + categoryFk + " not yet implemented");
467
	//					}
468

    
469
						//notes
470
						doCreatedUpdatedNotes(state, deb, rs);
471
						doId(state, deb, factId, "Fact");
472

    
473
						//TODO
474
						//Designation References -> unclear how to map to CDM
475

    
476

    
477
						//sequence -> textData is not an identifiable entity therefore extensions are not possible
478
						//fact category better
479

    
480
						taxaToSave.add(taxonBase);
481
					}
482
				} catch (Exception re){
483
					logger.error("An exception occurred during the facts import");
484
					re.printStackTrace();
485
					success = false;
486
				}
487
				//put
488
			}
489
			logger.info("Facts handled: " + (i-1));
490
			logger.info("Taxa to save: " + taxaToSave.size());
491
			getTaxonService().save(taxaToSave);
492
		}catch(SQLException e){
493
			throw new RuntimeException(e);
494
		}
495
		return success;
496
	}
497

    
498

    
499
	/**
500
     * @param taxonDescription
501
     * @param salvadorDistribution
502
     */
503
    private void mergeSalvadorDistribution(TaxonDescription taxonDescription,
504
            @NotNull Distribution newDistribution) {
505

    
506
        Distribution existingDistribution = null;
507
        for (DescriptionElementBase deb : taxonDescription.getElements()){
508
            if (deb.isInstanceOf(Distribution.class)){
509
                Distribution distribution = CdmBase.deproxy(deb, Distribution.class);
510
                if (distribution.getArea() != null && distribution.getArea().equals(newDistribution.getArea())){
511
                    existingDistribution = distribution;
512
                    break;
513
                }else if (distribution.getArea() == null){
514
                    logger.warn("Area for distribution is null: " + distribution.getUuid());
515
                }
516
            }
517
        }
518
        if (existingDistribution == null){
519
            taxonDescription.addElement(newDistribution);
520
        }else if(!existingDistribution.getStatus().equals(newDistribution.getStatus())){
521
            //should not happen
522
            logger.warn("Taxon has areas with different distribution states: " + taxonDescription.getTaxon().getTitleCache());
523
        }else{
524
            //do nothing, distribution already exists
525
        }
526
    }
527

    
528

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

    
567
    private PresenceAbsenceTerm getSalvadorDistributionState(Taxon taxon) {
568
        boolean hasGlobalDist = false;
569
        for (TaxonDescription desc : taxon.getDescriptions()){
570
            for (DescriptionElementBase deb : desc.getElements()){
571
                if (deb.getFeature().getUuid().equals(BerlinModelTransformer.uuidFeatureDistributionGlobal)){
572
                    hasGlobalDist = true;
573
                    TextData textData = CdmBase.deproxy(deb, TextData.class);
574
                    for (LanguageString text : textData.getMultilanguageText().values()){
575
                        if (text.getText().contains("El Salvador")){
576
                            return PresenceAbsenceTerm.NATIVE();
577
                        }
578
                    }
579
                }
580
            }
581
        }
582
        if (!hasGlobalDist){
583
            logger.warn("No global distribution found: " + taxon.getTitleCache());
584
        }
585
        return hasGlobalDist ? PresenceAbsenceTerm.CULTIVATED(): PresenceAbsenceTerm.PRESENT();
586
    }
587

    
588

    
589
    /**
590
     * @param factId
591
     * @return
592
     */
593
    private Language getSalvadorFactLanguage(int categoryFkInt) {
594
        if (categoryFkInt == 350){
595
            return Language.ENGLISH();
596
        }else if (categoryFkInt == 1800 || categoryFkInt == 1900){
597
            return Language.UNDETERMINED();
598
        }
599
        return Language.SPANISH_CASTILIAN();
600
    }
601

    
602

    
603
    private TaxonDescription getMyTaxonDescripion(TaxonBase taxonBase, BerlinModelImportState state, Integer categoryFk, Integer taxonId, int factId, String fact, Reference sourceRef) {
604
		Taxon taxon = null;
605
		if ( taxonBase instanceof Taxon ) {
606
			taxon = (Taxon) taxonBase;
607
		}else{
608
			logger.warn("TaxonBase " + (taxonId==null?"(null)":taxonId) + " for Fact " + factId + " was not of type Taxon but: " + taxonBase.getClass().getSimpleName());
609
			return null;
610
		}
611

    
612
		TaxonDescription taxonDescription = null;
613
		Set<TaxonDescription> descriptionSet= taxon.getDescriptions();
614

    
615
		Media media = null;
616
		//for diptera / salvador images
617
		if (categoryFk == 51 || categoryFk == 312){  //TODO check also FactCategory string
618
			media = Media.NewInstance();
619
			taxonDescription = makeImage(state, fact, media, descriptionSet, taxon);
620

    
621
			if (taxonDescription == null){
622
				return null;
623
			}
624

    
625
			TextData textData = null;
626
			for (DescriptionElementBase el:  taxonDescription.getElements()){
627
				if (el.isInstanceOf(TextData.class)){
628
					textData = CdmBase.deproxy(el, TextData.class);
629
				}
630
			}
631
			if (textData == null){
632
				textData = TextData.NewInstance(Feature.IMAGE());
633
				taxonDescription.addElement(textData);
634
			}
635
			textData.addMedia(media);
636
		}
637
		//all others (no image) -> getDescription
638
		else{
639
			boolean isPublic = isPublicFeature(categoryFk);
640
		    for (TaxonDescription desc: descriptionSet){
641

    
642
			    if (! desc.isImageGallery()){
643
					if (state.getConfig().isSalvador()){
644
					    if (desc.isDefault() && isPublic || !desc.isDefault() && !isPublic){
645
					        taxonDescription = desc;
646
					        break;
647
					    }
648
					}else{
649
					    taxonDescription = desc;
650
					    break;
651
					}
652
				}
653
			}
654
			if (taxonDescription == null){
655
				taxonDescription = TaxonDescription.NewInstance();
656
				taxonDescription.setTitleCache(sourceRef == null ? null : sourceRef.getTitleCache(), true);
657
				if (state.getConfig().isSalvador()){
658
				    String title = "Factual data for " + taxon.getName().getTitleCache();
659
				    if (isPublic){
660
				        taxonDescription.setDefault(isPublic);
661
				    }else{
662
				        title = "Non public f" + title.substring(1);
663
				    }
664
				    taxonDescription.setTitleCache(title, true);
665
				}
666
				taxon.addDescription(taxonDescription);
667
			}
668
		}
669
		return taxonDescription;
670
	}
671

    
672

    
673
    /**
674
     * @param categoryFk
675
     * @return
676
     */
677
    private boolean isPublicFeature(Integer categoryFk) {
678
        return ! (categoryFk == 1800 || categoryFk == 1900 || categoryFk == 2000);
679
    }
680

    
681

    
682
	@Override
683
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
684
		String nameSpace;
685
		Class<?> cdmClass;
686
		Set<String> idSet;
687
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
688

    
689
		try{
690
			Set<String> taxonIdSet = new HashSet<String>();
691
			Set<String> referenceIdSet = new HashSet<String>();
692
			Set<String> refDetailIdSet = new HashSet<String>();
693
			while (rs.next()){
694
				handleForeignKey(rs, taxonIdSet, "taxonId");
695
				handleForeignKey(rs, referenceIdSet, "FactRefFk");
696
				handleForeignKey(rs, referenceIdSet, "PTDesignationRefFk");
697
				handleForeignKey(rs, refDetailIdSet, "FactRefDetailFk");
698
				handleForeignKey(rs, refDetailIdSet, "PTDesignationRefDetailFk");
699
		}
700

    
701
			//taxon map
702
			nameSpace = BerlinModelTaxonImport.NAMESPACE;
703
			cdmClass = TaxonBase.class;
704
			idSet = taxonIdSet;
705
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
706
			result.put(nameSpace, taxonMap);
707

    
708
			//reference map
709
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
710
			cdmClass = Reference.class;
711
			idSet = referenceIdSet;
712
			Map<String, Reference> referenceMap = (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
713
			result.put(nameSpace, referenceMap);
714

    
715
			//refDetail map
716
			nameSpace = BerlinModelRefDetailImport.REFDETAIL_NAMESPACE;
717
			cdmClass = Reference.class;
718
			idSet = refDetailIdSet;
719
			Map<String, Reference> refDetailMap= (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
720
			result.put(nameSpace, refDetailMap);
721

    
722
		} catch (SQLException e) {
723
			throw new RuntimeException(e);
724
	}
725
		return result;
726
	}
727

    
728

    
729
	/**
730
	 * @param state
731
	 * @param media
732
	 * @param media
733
	 * @param descriptionSet
734
	 * @throws URISyntaxException
735
	 *
736
	 */
737
	private TaxonDescription makeImage(BerlinModelImportState state, String fact, Media media, Set<TaxonDescription> descriptionSet, Taxon taxon) {
738
		TaxonDescription taxonDescription = null;
739
		try {
740
	        Reference sourceRef = state.getTransactionalSourceReference();
741
    		URI uri;
742
    		URI thumbUri;
743
    		if (state.getConfig().isSalvador()){
744
    		    String thumbs = "thumbs/";
745
    		    String uriStrFormat = "http://media.e-taxonomy.eu/salvador/berendsohn-et-al-%s/%s.jpg";
746
    		    Integer intFact = Integer.valueOf(fact);
747
    		    String vol = "2009";
748
    		    int page = intFact + 249;
749
                if (intFact >= 263){
750
    		        vol = "2016";
751
    		        page = intFact - (intFact < 403 ? 95 : 94);
752
    		    }else if (intFact >= 142){
753
    		        vol = "2012";
754
    		        page = intFact + (intFact < 255 ? 3 : 4);
755
    		    }
756

    
757
                String title = getSalvadorImageTitle(intFact, vol);
758
                media.putTitle(Language.LATIN(), title);
759
                String description = getSalvadorImageDescription(intFact);
760
                media.putDescription(Language.SPANISH_CASTILIAN(), description);
761

    
762
    		    Reference ref = getSalvadorReference(vol);
763
    		    String originalName = getSalvadorImageNameInfo(intFact);
764
    		    IdentifiableSource source = media.addSource(OriginalSourceType.PrimaryMediaSource, fact, "Fig.", ref, "p. " + page);
765
    		    source.setOriginalNameString(originalName);
766
    		    media.setArtist(getSalvadorArtist());
767
    		    media.addRights(getSalvadorCopyright(vol));
768
    		    String uriStr = String.format(uriStrFormat, vol, fact);
769
    		    String thumbUriStr = String.format(uriStrFormat, vol, thumbs + fact);
770
    		    uri = new URI(uriStr);
771
    		    thumbUri = new URI(thumbUriStr);
772
    		}else{
773
    		    uri = new URI(fact.trim());
774
    		    thumbUri = null;
775
    		}
776

    
777
    		makeMediaRepresentation(media, uri);
778
    		if (thumbUri != null){
779
                makeMediaRepresentation(media, thumbUri);
780
    		}
781

    
782
    		taxonDescription = taxon.getOrCreateImageGallery(sourceRef == null ? null :sourceRef.getTitleCache());
783
		} catch (URISyntaxException e) {
784
            logger.warn("URISyntaxException. Image could not be imported: " + fact);
785
            return null;
786
        }
787
		return taxonDescription;
788
	}
789

    
790

    
791
	/**
792
     * @param intFact
793
     * @param vol
794
     * @return
795
     */
796
    private String getSalvadorImageTitle(Integer intFact, String vol) {
797
        initSalvadorImagesFile();
798
        String[] line = salvadorImages.get(intFact);
799
        if (line == null){
800
            logger.warn("Could not find salvador image metadata for " + intFact);
801
            return String.valueOf(intFact);
802
        }else{
803
            String name = getSalvadorImageNameInfo(intFact);
804
            String result = UTF8.ENGLISH_QUOT_START +  name + UTF8.ENGLISH_QUOT_END + " [Berendsohn & al. " + vol + "]";
805
            return result;
806
        }
807
    }
808

    
809

    
810
    private Map<Integer, String[]> salvadorImages = null;
811
    private String getSalvadorImageDescription(Integer intFact) {
812
        initSalvadorImagesFile();
813
        String[] line = salvadorImages.get(intFact);
814
        if (line == null){
815
            logger.warn("Could not find salvador image metadata for " + intFact);
816
            return String.valueOf(intFact);
817
        }else{
818
            int i = 2;
819
            String result = CdmUtils.concat(" " + UTF8.EN_DASH + " ", line[i], line[i + 1]);
820
            return result;
821
        }
822
    }
823

    
824
    private String getSalvadorImageNameInfo(Integer intFact) {
825
        initSalvadorImagesFile();
826
        String[] line = salvadorImages.get(intFact);
827
        if (line == null){
828
            logger.warn("Could not find salvador image metadata for " + intFact);
829
            return String.valueOf(intFact);
830
        }else{
831
            int i = 1;
832
            String result = line[i].substring("Fig. ".length() + line[0].length()).trim();
833
            return result;
834
        }
835
    }
836

    
837
    private void initSalvadorImagesFile() {
838
        if (salvadorImages == null){
839
            salvadorImages = new HashMap<>();
840
            try {
841
                CSVReader reader = new CSVReader(CdmUtils.getUtf8ResourceReader("salvador" + CdmUtils.getFolderSeperator() + "SalvadorImages.csv"),';');
842
                List<String[]> lines = reader.readAll();
843
                for (String[] line : lines){
844
                    String first = line[0];
845
                    if(! "ID".equals(first)){
846
                        try {
847
                            salvadorImages.put(Integer.valueOf(first), line);
848
                        } catch (NumberFormatException e) {
849
                            logger.warn("Number not recognized: " + first);
850
                        }
851
                    }
852
                }
853
                reader.close();
854
            } catch (IOException e) {
855
                e.printStackTrace();
856
            }
857
        }
858
    }
859

    
860

    
861
    private Rights rights1;
862
    private Rights rights2;
863
    private Rights rights3;
864

    
865
    private Rights getSalvadorCopyright(String vol) {
866
        initRights();
867
        if ("2009".equals(vol)){
868
            return rights1;
869
        }else if ("2012".equals(vol)){
870
            return rights1;
871
        }else if ("2016".equals(vol)){
872
            return rights3;
873
        }else{
874
            throw new RuntimeException("Volume not recognized: " + vol);
875
        }
876
    }
877

    
878
    private void initRights(){
879
        if (rights1 == null){
880
            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.";
881
            rights1 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
882
            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.";
883
            rights2 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
884
            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.";
885
            rights3 = Rights.NewInstance(text, Language.SPANISH_CASTILIAN(), RightsType.COPYRIGHT());
886
            getCommonService().save(rights1);
887
            getCommonService().save(rights2);
888
            getCommonService().save(rights3);
889
        }
890
    }
891

    
892
    private Integer salvadorArtistId;
893
    private AgentBase<?> getSalvadorArtist() {
894
        if (salvadorArtistId == null){
895
            Person person = Person.NewInstance();
896
            person.setFirstname("José Gerver");
897
            person.setLastname("Molina");
898
            salvadorArtistId = getAgentService().save(person).getId();
899
            return person;
900
        }else{
901
            return getAgentService().find(salvadorArtistId);
902
        }
903
    }
904

    
905
    private Integer salvadorRef1Id;
906
    private Integer salvadorRef2Id;
907
    private Integer salvadorRef3Id;
908

    
909
    private Reference getSalvadorReference(String vol){
910
        if (salvadorRef1Id == null){
911
            makeSalvadorReferences();
912
        }
913
        if ("2009".equals(vol)){
914
            return getReferenceService().find(salvadorRef1Id);
915
        }else if ("2012".equals(vol)){
916
            return getReferenceService().find(salvadorRef2Id);
917
        }else if ("2016".equals(vol)){
918
            return getReferenceService().find(salvadorRef3Id);
919
        }else{
920
            throw new RuntimeException("Volume not recognized: " + vol);
921
        }
922

    
923
    }
924

    
925
    private void makeSalvadorReferences() {
926
        Person walter = Person.NewTitledInstance("Berendsohn, W. G.");
927
        walter.setFirstname("Walter G.");
928
        walter.setLastname("Berendsohn");
929
        Person katja = Person.NewTitledInstance("Gruber, Anne Kathrina");
930
        katja.setFirstname("Anne Katharina");
931
        katja.setLastname("Gruber");
932
        Person monte = Person.NewTitledInstance("Monterrosa Salomón, J.");
933
        Person olmedo = Person.NewTitledInstance("Olmedo Galán, P.");
934
        Person rodriguez = Person.NewTitledInstance("Rodríguez Delcid, D");
935

    
936
        Team team1 = Team.NewInstance();
937
        team1.addTeamMember(walter);
938
        team1.addTeamMember(katja);
939
        team1.addTeamMember(monte);
940

    
941
        Team team2 = Team.NewInstance();
942
        team2.addTeamMember(walter);
943
        team2.addTeamMember(katja);
944
        team2.addTeamMember(rodriguez);
945
        team2.addTeamMember(olmedo);
946

    
947
        Reference vol1 = ReferenceFactory.newBook();
948
        Reference vol2 = ReferenceFactory.newBook();
949
        Reference vol3 = ReferenceFactory.newBook();
950

    
951
        vol1.setAuthorship(team1);
952
        vol2.setAuthorship(team1);
953
        vol3.setAuthorship(team2);
954

    
955
        vol1.setDatePublished(TimePeriodParser.parseString("2009"));
956
        vol2.setDatePublished(TimePeriodParser.parseString("2012"));
957
        vol3.setDatePublished(TimePeriodParser.parseString("2016"));
958

    
959
        Reference englera = ReferenceFactory.newPrintSeries("Englera");
960
        vol1.setInSeries(englera);
961
        vol2.setInSeries(englera);
962
        vol3.setInSeries(englera);
963

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

    
968
        vol1.setVolume("29(1)");
969
        vol2.setVolume("29(2)");
970
        vol3.setVolume("29(3)");
971

    
972
        vol1.setPages("1-438");
973
        vol2.setVolume("1-300");
974
        vol3.setVolume("1-356");
975

    
976
        String placePublished = "Berlin: Botanic Garden and Botanical Museum Berlin; Antiguo Cuscatlán: Asociación Jardín Botánico La Laguna, El Salvador";
977
        vol1.setPlacePublished(placePublished);
978
        vol2.setPlacePublished(placePublished);
979
        vol3.setPlacePublished(placePublished);
980

    
981
        salvadorRef1Id = getReferenceService().save(vol1).getId();
982
        salvadorRef2Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol2)).getId();
983
        salvadorRef3Id = getReferenceService().find(getReferenceService().saveOrUpdate(vol3)).getId();
984
        return;
985
    }
986

    
987

    
988
    /**
989
     * @param media
990
     * @param uri
991
     * @param imageInfo
992
     * @param size
993
     */
994
    private void makeMediaRepresentation(Media media, URI uri) {
995
        ImageInfo imageInfo = null;
996
        Integer size = null;
997
        try {
998
            imageInfo = ImageInfo.NewInstance(uri, 30);
999
        } catch (IOException | HttpException e) {
1000
            logger.error("Error when reading image meta: " + e + ", "+ uri.toString());
1001
        }
1002
        String mimeType = imageInfo == null ? null : imageInfo.getMimeType();
1003
        String suffix = imageInfo == null ? null : imageInfo.getSuffix();
1004
        MediaRepresentation mediaRepresentation = MediaRepresentation.NewInstance(mimeType, suffix);
1005
        media.addRepresentation(mediaRepresentation);
1006
        ImageFile image = ImageFile.NewInstance(uri, size, imageInfo);
1007
        mediaRepresentation.addRepresentationPart(image);
1008
    }
1009

    
1010
	private TaxonBase<?> getTaxon(Map<String, TaxonBase> taxonMap, Integer taxonIdObj, Number taxonId){
1011
		if (taxonIdObj != null){
1012
			return taxonMap.get(String.valueOf(taxonId));
1013
		}else{
1014
			return null;
1015
		}
1016

    
1017
	}
1018

    
1019
	private Feature getFeature(Map<Integer, Feature>  featureMap, Integer categoryFkInt){
1020
		if (categoryFkInt != null){
1021
			return featureMap.get(categoryFkInt);
1022
		}else{
1023
			return null;
1024
		}
1025

    
1026
	}
1027

    
1028
	@Override
1029
	protected boolean doCheck(BerlinModelImportState state){
1030
		IOValidator<BerlinModelImportState> validator = new BerlinModelFactsImportValidator();
1031
		return validator.validate(state);
1032
	}
1033

    
1034
	@Override
1035
	protected boolean isIgnore(BerlinModelImportState state){
1036
		return ! state.getConfig().isDoFacts();
1037
	}
1038

    
1039

    
1040

    
1041
}
(4-4/21)