Project

General

Profile

Download (58.4 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.io.berlinModel.in;
10

    
11
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_ARTICLE;
12
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_BOOK;
13
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_CONFERENCE_PROCEEDINGS;
14
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_DATABASE;
15
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_INFORMAL;
16
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_JOURNAL;
17
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_JOURNAL_VOLUME;
18
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_PART_OF_OTHER_TITLE;
19
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_PRINT_SERIES;
20
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_UNKNOWN;
21
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_WEBSITE;
22
import static eu.etaxonomy.cdm.io.common.ImportHelper.NO_OVERWRITE;
23
import static eu.etaxonomy.cdm.io.common.ImportHelper.OBLIGATORY;
24
import static eu.etaxonomy.cdm.io.common.ImportHelper.OVERWRITE;
25

    
26
import java.net.URISyntaxException;
27
import java.sql.ResultSet;
28
import java.sql.SQLException;
29
import java.util.ArrayList;
30
import java.util.Arrays;
31
import java.util.HashMap;
32
import java.util.HashSet;
33
import java.util.List;
34
import java.util.Map;
35
import java.util.Set;
36
import java.util.UUID;
37
import java.util.regex.Matcher;
38
import java.util.regex.Pattern;
39

    
40
import org.apache.logging.log4j.LogManager;
41
import org.apache.logging.log4j.Logger;
42
import org.springframework.stereotype.Component;
43

    
44
import eu.etaxonomy.cdm.common.CdmUtils;
45
import eu.etaxonomy.cdm.common.DOI;
46
import eu.etaxonomy.cdm.common.URI;
47
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
48
import eu.etaxonomy.cdm.io.berlinModel.in.validation.BerlinModelReferenceImportValidator;
49
import eu.etaxonomy.cdm.io.common.ICdmImport;
50
import eu.etaxonomy.cdm.io.common.IImportConfigurator;
51
import eu.etaxonomy.cdm.io.common.ImportHelper;
52
import eu.etaxonomy.cdm.io.common.ResultSetPartitioner;
53
import eu.etaxonomy.cdm.io.common.Source;
54
import eu.etaxonomy.cdm.io.common.mapping.CdmAttributeMapperBase;
55
import eu.etaxonomy.cdm.io.common.mapping.CdmIoMapping;
56
import eu.etaxonomy.cdm.io.common.mapping.CdmSingleAttributeMapperBase;
57
import eu.etaxonomy.cdm.io.common.mapping.DbImportExtensionMapper;
58
import eu.etaxonomy.cdm.io.common.mapping.DbImportMarkerMapper;
59
import eu.etaxonomy.cdm.io.common.mapping.DbSingleAttributeImportMapperBase;
60
import eu.etaxonomy.cdm.io.common.mapping.berlinModel.CdmOneToManyMapper;
61
import eu.etaxonomy.cdm.io.common.mapping.berlinModel.CdmStringMapper;
62
import eu.etaxonomy.cdm.io.common.mapping.berlinModel.CdmUriMapper;
63
import eu.etaxonomy.cdm.io.common.mapping.berlinModel.CdmUuidMapper;
64
import eu.etaxonomy.cdm.model.agent.Person;
65
import eu.etaxonomy.cdm.model.agent.Team;
66
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
67
import eu.etaxonomy.cdm.model.common.CdmBase;
68
import eu.etaxonomy.cdm.model.common.ExtensionType;
69
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
70
import eu.etaxonomy.cdm.model.common.Identifier;
71
import eu.etaxonomy.cdm.model.common.Marker;
72
import eu.etaxonomy.cdm.model.common.MarkerType;
73
import eu.etaxonomy.cdm.model.reference.IArticle;
74
import eu.etaxonomy.cdm.model.reference.IBookSection;
75
import eu.etaxonomy.cdm.model.reference.IPrintSeries;
76
import eu.etaxonomy.cdm.model.reference.Reference;
77
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
78
import eu.etaxonomy.cdm.model.reference.ReferenceType;
79
import eu.etaxonomy.cdm.model.term.IdentifierType;
80
import eu.etaxonomy.cdm.model.term.TermVocabulary;
81
import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
82
import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
83

    
84
/**
85
 * @author a.mueller
86
 * @since 20.03.2008
87
 */
88
@Component
89
public class BerlinModelReferenceImport extends BerlinModelImportBase {
90

    
91
    private static final long serialVersionUID = -3667566958769967591L;
92
    private static final Logger logger = LogManager.getLogger();
93

    
94
	public static final String REFERENCE_NAMESPACE = "Reference";
95
	private static final String REF_AUTHOR_NAMESPACE = "Reference.refAuthorString";
96

    
97
	public static final UUID REF_DEPOSITED_AT_UUID = UUID.fromString("23ca88c7-ce73-41b2-8ca3-2cb22f013beb");
98
	public static final UUID REF_SOURCE_UUID = UUID.fromString("d6432582-2216-4b08-b0db-76f6c1013141");
99
	public static final UUID DATE_STRING_UUID = UUID.fromString("e4130eae-606e-4b0c-be4f-e93dc161be7d");
100
	public static final UUID IS_PAPER_UUID = UUID.fromString("8a326129-d0d0-4f9d-bbdf-8d86b037c65e");
101

    
102
	private final int modCount = 1000;
103
	private static final String pluralString = "references";
104
	private static final String dbTableName = "reference";
105

    
106
	public BerlinModelReferenceImport(){
107
		super(dbTableName, pluralString);
108
	}
109

    
110
	protected void initializeMappers(BerlinModelImportState state){
111
		for (CdmAttributeMapperBase mapper: classMappers){
112
			if (mapper instanceof DbSingleAttributeImportMapperBase){
113
				@SuppressWarnings("unchecked")
114
                DbSingleAttributeImportMapperBase<BerlinModelImportState,Reference> singleMapper =
115
				        (DbSingleAttributeImportMapperBase<BerlinModelImportState,Reference>)mapper;
116
				singleMapper.initialize(state, Reference.class);
117
			}
118
		}
119
		return;
120
	}
121

    
122
	private Set<Integer> commonNameRefSet = null;
123
	private void initializeCommonNameRefMap(BerlinModelImportState state) throws SQLException{
124
	    if (state.getConfig().isEuroMed()){
125
	        commonNameRefSet = new HashSet<>();
126
	        String queryStr = "SELECT DISTINCT RefFk "
127
	                + " FROM emCommonName ";
128
	        ResultSet rs = state.getConfig().getSource().getResultSet(queryStr);
129
	        while (rs.next()){
130
	            commonNameRefSet.add(rs.getInt("RefFk"));
131
	        }
132
	    }
133
	}
134

    
135
	protected static CdmAttributeMapperBase[] classMappers = new CdmAttributeMapperBase[]{
136
		new CdmStringMapper("edition", "edition"),
137
		new CdmStringMapper("volume", "volume"),
138
		new CdmStringMapper("publisher", "publisher"),
139
		new CdmStringMapper("publicationTown", "placePublished"),
140
		new CdmStringMapper("isbn", "isbn"),
141
		new CdmStringMapper("isbn", "isbn"),
142
		new CdmStringMapper("pageString", "pages"),
143
		new CdmStringMapper("series", "seriesPart"),
144
		new CdmStringMapper("issn", "issn"),
145
		new CdmUriMapper("url", "uri"),
146
		new CdmUuidMapper("uuid", "uuid"),
147
		DbImportExtensionMapper.NewInstance("NomStandard", ExtensionType.NOMENCLATURAL_STANDARD()),
148
		DbImportExtensionMapper.NewInstance("DateString", DATE_STRING_UUID, "Date String", "Date String", "dates"),
149
		DbImportExtensionMapper.NewInstance("RefDepositedAt", REF_DEPOSITED_AT_UUID, "Ref. deposited at", "Reference is deposited at", "at"),
150
		DbImportExtensionMapper.NewInstance("RefSource", REF_SOURCE_UUID, "RefSource", "Reference Source", "source"),
151
		DbImportMarkerMapper.NewInstance("isPaper", IS_PAPER_UUID, "is paper", "is paper", "paper", false)
152
		//not yet supported by model
153
        ,new CdmStringMapper("refAuthorString", "refAuthorString"),
154
	};
155

    
156
	protected static String[] operationalAttributes = new String[]{
157
		"refId", "refCache", "nomRefCache", "preliminaryFlag", "inRefFk", "title", "nomTitleAbbrev",
158
		"refAuthorString", "nomAuthorTeamFk",
159
		"refCategoryFk", "thesisFlag", "informalRefCategory", "idInSource"
160
	};
161

    
162
	protected static String[] createdAndNotesAttributes = new String[]{
163
			"created_When", "updated_When", "created_Who", "updated_Who", "notes"
164
	};
165

    
166
	protected static String[] unclearMappers = new String[]{
167
			/*"isPaper",*/ "exportDate",
168
	};
169

    
170
	//TODO isPaper
171
	//
172

    
173
	//type to count the references nomReferences that have been created and saved
174
	private class RefCounter{
175
		RefCounter() {refCount = 0;}
176
		int refCount;
177
		int dedupCount;
178

    
179
		@Override
180
        public String toString(){return String.valueOf(refCount) + "/" + String.valueOf(dedupCount) ;}
181
	}
182

    
183
	@Override
184
	protected String getRecordQuery(BerlinModelImportConfigurator config) {
185
		return null;  //not needed
186
	}
187

    
188
	@Override
189
	protected void doInvoke(BerlinModelImportState state){
190
		logger.info("start make " + getPluralString() + " ...");
191

    
192
		boolean success = true;
193
		initializeMappers(state);
194
		try {
195
            initializeCommonNameRefMap(state);
196
        } catch (SQLException e1) {
197
            e1.printStackTrace();
198
            logger.error("Error in initializeCommonNameRefMap in BerlinModelReferenceimport");
199
        }
200
		BerlinModelImportConfigurator config = state.getConfig();
201
		Source source = config.getSource();
202

    
203
		String strSelectId = " SELECT Reference.RefId as refId ";
204
		String strSelectFull =
205
			" SELECT Reference.* ,InReference.RefCategoryFk as InRefCategoryFk, RefSource.RefSource " ;
206
		String strFrom =
207
		        " FROM %s  " +
208
		    	    " LEFT OUTER JOIN Reference as InReference ON InReference.refId = Reference.inRefFk " +
209
		    	    " LEFT OUTER JOIN RefSource ON Reference.RefSourceFk = RefSource.RefSourceId " +
210
		    	" WHERE (1=1) ";
211
		String strOrderBy = " ORDER BY InReference.inRefFk, Reference.inRefFk "; //to make in-references available in first run
212
		String strWherePartitioned = " AND (Reference.refId IN ("+ ID_LIST_TOKEN + ") ) ";
213

    
214
		String referenceTable = CdmUtils.Nz(state.getConfig().getReferenceIdTable());
215
		referenceTable = referenceTable.isEmpty() ? " Reference"  : referenceTable + " as Reference ";
216
		String strIdFrom = String.format(strFrom, referenceTable );
217

    
218
		String referenceFilter = CdmUtils.Nz(state.getConfig().getReferenceIdTable());
219
		if (! referenceFilter.isEmpty()){
220
			referenceFilter = " AND " + referenceFilter + " ";
221
		}
222
		referenceFilter = "";  //don't use it for now, in E+M the tabelle is directly used
223

    
224
		String strIdQueryFirstPath = strSelectId + strIdFrom + strOrderBy ;
225
		String strIdQuerySecondPath = strSelectId + strIdFrom + " AND (Reference.InRefFk is NOT NULL) ";
226

    
227
//		if (config.getDoReferences() == CONCEPT_REFERENCES){
228
//			strIdQueryNoInRef += " AND ( Reference.refId IN ( SELECT ptRefFk FROM PTaxon) ) " + referenceFilter;
229
//		}
230

    
231
		String strRecordQuery = strSelectFull + String.format(strFrom, " Reference ") + strWherePartitioned + strOrderBy;
232

    
233
		int recordsPerTransaction = config.getRecordsPerTransaction();
234
		try{
235
			//firstPath
236
			ResultSetPartitioner<BerlinModelImportState> partitioner =
237
			        ResultSetPartitioner.NewInstance(source, strIdQueryFirstPath, strRecordQuery, recordsPerTransaction);
238
			while (partitioner.nextPartition()){
239
				partitioner.doPartition(this, state);
240
			}
241
			logger.info("end make references without in-references ... " + getSuccessString(success));
242
			state.setReferenceSecondPath(true);
243

    
244
			//secondPath
245
//			partitioner = ResultSetPartitioner.NewInstance(source, strIdQuerySecondPath, strRecordQuery, recordsPerTransaction);
246
//			while (partitioner.nextPartition()){
247
//			    //currently not used as inRef assignment fully works through sorting of idQuery now, at least in E+M
248
//				partitioner.doPartition(this, state);
249
//			}
250
//			logger.info("end make references with no 1 in-reference ... " + getSuccessString(success));
251
			state.setReferenceSecondPath(false);
252
			logger.warn("Parsed book volumes: " + parsedBookVolumes);
253
		} catch (SQLException e) {
254
			logger.error("SQLException:" +  e);
255
			state.setUnsuccessfull();
256
	        return;
257
		}
258
		logger.info("end make " + getPluralString() + " ... " + getSuccessString(success));
259
		if (! success){
260
			state.setUnsuccessfull();
261
		}
262
		return;
263
	}
264

    
265
    @Override
266
	public boolean doPartition(@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner, BerlinModelImportState state) {
267
        state.getDeduplicationHelper().restartSession();
268

    
269
	    if (state.isReferenceSecondPath()){
270
			return doPartitionSecondPath(partitioner, state);
271
		}
272
		boolean success = true;
273

    
274
		Map<Integer, Reference> refToSave = new HashMap<>();
275

    
276
		BerlinModelImportConfigurator config = state.getConfig();
277

    
278
		try {
279

    
280
			int i = 0;
281
			RefCounter refCounter  = new RefCounter();
282
			ResultSet rs = partitioner.getResultSet();
283

    
284
			//for each resultset
285
			while (rs.next()){
286
				if ((i++ % modCount) == 0 && i!= 1 ){ logger.info("References handled: " + (i-1) + " in round -" );}
287

    
288
				success &= makeSingleReferenceRecord(rs, state, partitioner, refToSave, refCounter);
289
			} // end resultSet
290

    
291
			//for the concept reference a fixed uuid may be needed -> change uuid
292
			Integer sourceSecId = (Integer)config.getSourceSecId();
293
			Reference sec = refToSave.get(sourceSecId);
294

    
295
			if (sec != null){
296
				sec.setUuid(config.getSecUuid());
297
				logger.info("SecUuid changed to: " + config.getSecUuid());
298
			}
299

    
300
			//save and store in map
301
			logger.warn("Save references (" + refCounter.toString() + ")");  //set preliminary to warn for printing dedup count
302

    
303
			getReferenceService().saveOrUpdate(refToSave.values());
304

    
305
//			logger.info("end makeReferences ..." + getSuccessString(success));;
306
			return success;
307
		} catch (SQLException e) {
308
			logger.error("SQLException:" +  e);
309
			return false;
310
		}
311
	}
312

    
313

    
314

    
315
	/**
316
	 * Adds the inReference to the according references.
317
	 * @param partitioner
318
	 * @param state
319
	 * @return
320
	 */
321
	private boolean doPartitionSecondPath(@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner, BerlinModelImportState state) {
322
		boolean success = true;
323

    
324
		Map<Integer, Reference> refToSave = new HashMap<>();
325

    
326
		@SuppressWarnings("unchecked")
327
        Map<String, Reference> relatedReferencesMap = partitioner.getObjectMap(REFERENCE_NAMESPACE);
328

    
329
		try {
330
				int i = 0;
331
				RefCounter refCounter  = new RefCounter();
332

    
333
				ResultSet rs = partitioner.getResultSet();
334
				//for each resultset
335
				while (rs.next()){
336
					if ((i++ % modCount) == 0 && i!= 1 ){ logger.info("References handled: " + (i-1) + " in round -" );}
337

    
338
					Integer refId = rs.getInt("refId");
339
					Integer inRefFk = nullSafeInt(rs, "inRefFk");
340

    
341
					if (inRefFk != null){
342

    
343
						Reference thisRef = relatedReferencesMap.get(String.valueOf(refId));
344

    
345
						Reference inRef = relatedReferencesMap.get(String.valueOf(inRefFk));
346

    
347
						if (thisRef != null){
348
							if (inRef == null){
349
								logger.warn("No InRef found for nomRef: " + thisRef.getTitleCache() + "; RefId: " +  refId + "; inRefFK: " +  inRefFk);
350
							}
351
							thisRef.setInReference(inRef);
352
							refToSave.put(refId, thisRef);
353
							if(!thisRef.isProtectedTitleCache()){
354
							    thisRef.setTitleCache(null);
355
							    thisRef.getTitleCache();
356
							}
357
						}else{
358
						    logger.warn("Reference which has an inReference not found in DB. RefId: " + refId);
359
						}
360
						if(inRefFk.equals(0)){
361
						    logger.warn("InRefFk is 0 for refId "+ refId);
362
						}
363
					}
364

    
365
				} // end resultSet
366

    
367
				//save and store in map
368
				logger.info("Save in references (" + refCounter.toString() + ")");
369
				getReferenceService().saveOrUpdate(refToSave.values());
370

    
371
//			}//end resultSetList
372

    
373
//			logger.info("end makeReferences ..." + getSuccessString(success));;
374
			return success;
375
		} catch (SQLException e) {
376
			logger.error("SQLException:" +  e);
377
			return false;
378
		}
379
	}
380

    
381

    
382
	@Override
383
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
384

    
385
	    String nameSpace;
386
		Set<String> idSet;
387

    
388
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<>();
389

    
390
		try{
391
			Set<String> teamIdSet = new HashSet<>();
392
			Set<String> referenceIdSet = new HashSet<>();
393
			Set<String> teamStringSet = new HashSet<>();
394

    
395
			while (rs.next()){
396
				handleForeignKey(rs, teamIdSet, "NomAuthorTeamFk");
397
				handleForeignKey(rs, referenceIdSet, "InRefFk");
398
				handleForeignKey(rs, teamStringSet, "refAuthorString");
399
				//TODO only needed in second path but state not available here to check if state is second path
400
				handleForeignKey(rs, referenceIdSet, "refId");
401
			}
402

    
403
			Set<String> teamStringSet2 = new HashSet<>();
404
			for (String teamString : teamStringSet){
405
			    teamStringSet2.add(teamString.replace("'", "´"));
406
			}
407

    
408
			//team map
409
			nameSpace = BerlinModelAuthorTeamImport.NAMESPACE;
410
			idSet = teamIdSet;
411
            @SuppressWarnings("rawtypes")
412
            Map<String, TeamOrPersonBase> teamMap = getCommonService().getSourcedObjectsByIdInSourceC(TeamOrPersonBase.class, idSet, nameSpace);
413
			result.put(nameSpace, teamMap);
414

    
415
            //refAuthor map
416
            nameSpace = REF_AUTHOR_NAMESPACE;
417
            idSet = teamStringSet2;
418
            @SuppressWarnings("unchecked")
419
            Map<String, TeamOrPersonBase> refAuthorMap = getCommonService().getSourcedObjectsByIdInSourceC(TeamOrPersonBase.class, idSet, nameSpace);
420
            result.put(nameSpace, refAuthorMap);
421

    
422
			//reference map
423
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
424
			idSet = referenceIdSet;
425
			Map<String, Reference> referenceMap = getCommonService().getSourcedObjectsByIdInSourceC(Reference.class, idSet, nameSpace);
426
			result.put(nameSpace, referenceMap);
427

    
428
		} catch (SQLException e) {
429
			throw new RuntimeException(e);
430
		}
431
		return result;
432
	}
433

    
434

    
435
	/**
436
	 * Handles a single reference record
437
	 * @param rs
438
	 * @param state
439
	 * @param biblioRefToSave
440
	 * @param nomRefToSave
441
	 * @param relatedBiblioReferences
442
	 * @param relatedNomReferences
443
	 * @param refCounter
444
	 * @return
445
	 */
446
	private boolean makeSingleReferenceRecord(
447
				ResultSet rs,
448
				BerlinModelImportState state,
449
				ResultSetPartitioner<BerlinModelImportState> partitioner,
450
				Map<Integer, Reference> refToSave,
451
				RefCounter refCounter){
452

    
453
	    boolean success = true;
454

    
455
		Integer refId = null;
456
		try {
457
			Map<String, Object> valueMap = getValueMap(rs);
458

    
459
			Integer categoryFk = (Integer)valueMap.get("refCategoryFk".toLowerCase());
460
			refId = (Integer)valueMap.get("refId".toLowerCase());
461
			Boolean thesisFlag = (Boolean)valueMap.get("thesisFlag".toLowerCase());
462

    
463

    
464
			Reference reference;
465
			logger.debug("RefCategoryFk: " + categoryFk);
466

    
467
			if (thesisFlag){
468
				reference = makeThesis(valueMap);
469
			}else if (categoryFk == REF_JOURNAL){
470
				reference = makeJournal(valueMap);
471
			}else if(categoryFk == REF_BOOK){
472
				reference = makeBook(valueMap);
473
			}else if(categoryFk == REF_DATABASE){
474
				reference = makeDatabase(valueMap);
475
			}else if(categoryFk == REF_INFORMAL){
476
				reference = makeInformal(valueMap);
477
			}else if(categoryFk == REF_WEBSITE){
478
				reference = makeWebSite(valueMap);
479
			}else if(categoryFk == REF_UNKNOWN){
480
				reference = makeUnknown(valueMap);
481
			}else if(categoryFk == REF_PRINT_SERIES){
482
				reference = makePrintSeries(valueMap);
483
			}else if(categoryFk == REF_CONFERENCE_PROCEEDINGS){
484
				reference = makeProceedings(valueMap);
485
			}else if(categoryFk == REF_ARTICLE){
486
				reference = makeArticle(valueMap);
487
			}else if(categoryFk == REF_JOURNAL_VOLUME){
488
				reference = makeJournalVolume(valueMap);
489
			}else if(categoryFk == REF_PART_OF_OTHER_TITLE){
490
				reference = makePartOfOtherTitle(valueMap);
491
			}else{
492
				logger.warn("Unknown categoryFk (" + categoryFk + "). Create 'Generic instead'");
493
				reference = ReferenceFactory.newGeneric();
494
				success = false;
495
			}
496

    
497
			//refYear
498
			String refYear = (String)valueMap.get("refYear".toLowerCase());
499
			reference.setDatePublished(ImportHelper.getDatePublished(refYear));
500

    
501
			handleEdition(reference);
502

    
503
			//created, updated, notes
504
			doCreatedUpdatedNotes(state, reference, rs);
505

    
506
			//idInSource (import from older source to berlin model)
507
			//TODO do we want this being imported? Maybe as alternatvie identifier?
508
			String idInSource = (String)valueMap.get("IdInSource".toLowerCase());
509
			if (isNotBlank(idInSource)){
510
				if(!state.getConfig().isDoSourceNumber()){
511
				    IdentifiableSource source = IdentifiableSource.NewDataImportInstance(idInSource);
512
				    source.setIdNamespace("import to Berlin Model");
513
				    reference.addSource(source);
514
				}else{
515
				    makeSourceNumbers(state, idInSource, reference, refId);
516
				}
517
			}
518
            String uuid = null;
519
            if (resultSetHasColumn(rs,"UUID")){
520
                uuid = rs.getString("UUID");
521
                if (uuid != null){
522
                    reference.setUuid(UUID.fromString(uuid));
523
                }
524
            }
525

    
526
			//nom&BiblioReference  - must be last because a clone is created
527
			success &= makeNomAndBiblioReference(rs, state, partitioner, refId, reference, refCounter, refToSave);
528

    
529

    
530
		} catch (Exception e) {
531
			logger.warn("Reference with BM refId '" + CdmUtils.Nz(refId) +  "' threw Exception and could not be saved");
532
			e.printStackTrace();
533
			success = false;
534
		}
535
		return success;
536
	}
537

    
538
    private void makeSourceNumbers(BerlinModelImportState state, String idInSource, Reference reference,
539
            Integer refId) {
540
        String[] splits = idInSource.split("\\|");
541
        for (String split : splits){
542
            split = split.trim();
543
            UUID uuid = BerlinModelTransformer.uuidEMReferenceSourceNumber;
544
            TermVocabulary<IdentifierType> voc = null;  //user defined voc
545
            IdentifierType type = getIdentiferType(state, uuid, "E+M Reference Source Number", "Euro+Med Reference Source Number", "E+M Source Number", voc);
546
            Identifier.NewInstance(reference, split, type);
547
        }
548
    }
549

    
550
    private void handleEdition(Reference reference) {
551
        if (reference.getEdition()!= null && reference.getEdition().startsWith("ed. ")){
552
            reference.setEdition(reference.getEdition().substring(4));
553
        }
554

    
555
    }
556

    
557
    /**
558
	 * Creates and saves a nom. reference and a biblio. reference after checking necessity
559
	 * @param rs
560
	 * @param refId
561
	 * @param ref
562
	 * @param refCounter
563
	 * @param biblioRefToSave
564
	 * @param nomRefToSave
565
	 * @param teamMap
566
	 * @param stores
567
	 * @return
568
	 * @throws SQLException
569
	 */
570
	private boolean makeNomAndBiblioReference(
571
				ResultSet rs,
572
				BerlinModelImportState state,
573
				@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner,
574
				int refId,
575
				Reference ref,
576
				RefCounter refCounter,
577
				Map<Integer, Reference> refToSave
578
			) throws SQLException{
579

    
580
		@SuppressWarnings("unchecked")
581
        Map<String, Team> teamMap = partitioner.getObjectMap(BerlinModelAuthorTeamImport.NAMESPACE);
582

    
583
		String refCache = trim(rs.getString("refCache"));
584
		String nomRefCache = trim(rs.getString("nomRefCache"));
585
		String title = trim(rs.getString("title"));
586
		String nomTitleAbbrev = trim(rs.getString("nomTitleAbbrev"));
587
		boolean isPreliminary = rs.getBoolean("PreliminaryFlag");
588
		String refAuthorString = trim(rs.getString("refAuthorString"));
589
		Integer nomAuthorTeamFk = nullSafeInt(rs, "NomAuthorTeamFk");
590
		Integer inRefFk = nullSafeInt(rs, "inRefFk");
591

    
592

    
593
		TeamOrPersonBase<?> nomAuthor = null;
594
		if (nomAuthorTeamFk != null){
595
		    String strNomAuthorTeamFk = String.valueOf(nomAuthorTeamFk);
596
		    nomAuthor = teamMap.get(strNomAuthorTeamFk);
597
		    if (nomAuthor == null){
598
		        logger.warn("NomAuthor ("+strNomAuthorTeamFk+") not found in teamMap (but it should exist) for " + refId);
599
		    }
600
		}
601

    
602
		Reference sourceReference = state.getTransactionalSourceReference();
603

    
604
		//preliminary
605
		if (isPreliminary){
606
			ref.setAbbrevTitleCache(nomRefCache, true);
607
			ref.setTitleCache(refCache, true);
608
		}
609

    
610
		//title/abbrevTitle
611
		if (isNotBlank(nomTitleAbbrev)){
612
			ref.setAbbrevTitle(nomTitleAbbrev);
613
		}
614
		if (isNotBlank(title)){
615
			ref.setTitle(title);
616
		}
617

    
618
		//author
619
		TeamOrPersonBase<?> author = getAuthorship(state, refAuthorString, nomAuthor, refId);
620
		ref.setAuthorship(author);
621

    
622
		if (ref.getType().equals(ReferenceType.Book)){
623
		    extraktBookVolume(ref);
624
		}
625

    
626
		//inRef
627
		Reference inRef = null;
628
		if (inRefFk != null){
629
		    @SuppressWarnings({"unchecked" })
630
		    Map<String, Reference>  relatedReferences = partitioner.getObjectMap(REFERENCE_NAMESPACE);
631
		    inRef = relatedReferences.get(String.valueOf(inRefFk));
632
		    if (inRef == null){
633
		        inRef = refToSave.get(inRefFk);
634
		    }
635
		    if (inRef == null){
636
		        logger.warn("InRef not (yet) found. RefId: " + refId + "; InRef: "+ inRefFk);
637
		    }else{
638
		        ref.setInReference(inRef);
639
		    }
640
		}
641

    
642
		Reference result = deduplicateReference(state, ref);
643
		if(ref != result){
644
		    //dedup not possible at this point because inRef exists but is not yet defined
645
		    if (inRefFk != null && inRef == null){
646
		        result = ref;
647
		        logger.warn("Ref has deduplication candidate but inRef is still missing. " + inRef);
648
		    }else{
649
		        logger.debug("Reference was deduplicated. RefId: " + refId);
650
		        //FIXME also check annotations etc. for deduplication
651
		        refCounter.dedupCount++;
652
		    }
653
		}else{
654
		    refCounter.refCount++;
655
		}
656

    
657
		//save
658
		if (! refToSave.containsKey(refId)){
659
			refToSave.put(refId, result);
660
		}else{
661
		    //should not happen
662
			logger.warn("Duplicate refId in Berlin Model database. Second reference was not imported !!");
663
		}
664

    
665

    
666
		//refId
667
		ImportHelper.setOriginalSource(result, sourceReference, refId, REFERENCE_NAMESPACE);
668

    
669
		if (commonNameRefSet != null && commonNameRefSet.contains(refId)){
670
		    result.addMarker(Marker.NewInstance(MarkerType.COMMON_NAME_REFERENCE(), true));
671
        }
672

    
673
		return true;
674
	}
675

    
676
	/**
677
     * @param string
678
     * @return
679
     */
680
    private String trim(String string) {
681
        if (string == null){
682
            return null;
683
        }else{
684
            return string.trim();
685
        }
686
    }
687

    
688
    /**
689
	 * Copies the created and updated information from the nomReference to the cloned bibliographic reference
690
	 * @param referenceBase
691
	 * @param nomReference
692
	 */
693
	private void copyCreatedUpdated(Reference biblioReference, Reference nomReference) {
694
		biblioReference.setCreatedBy(nomReference.getCreatedBy());
695
		biblioReference.setCreated(nomReference.getCreated());
696
		biblioReference.setUpdatedBy(nomReference.getUpdatedBy());
697
		biblioReference.setUpdated(nomReference.getUpdated());
698

    
699
	}
700

    
701
	private Reference makeArticle (Map<String, Object> valueMap){
702

    
703
		IArticle article = ReferenceFactory.newArticle();
704
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
705
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
706
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
707

    
708
		if (inRefFk != null){
709
			if (inRefCategoryFk != REF_JOURNAL){
710
				logger.warn("Wrong inrefCategory for Article (refID = " + refId +"). Type must be 'Journal' but was not (RefCategoryFk=" + inRefCategoryFk + "))." +
711
					" InReference was added anyway! ");
712
			}
713
		}else{
714
			logger.warn ("Article has no inreference: " + refId);
715
		}
716
		makeStandardMapper(valueMap, (Reference)article); //url, pages, series, volume
717
		String url = (String)valueMap.get("url");
718
		if (url != null && url.contains("dx.doi.org")){
719
		    article.setDoi(DOI.fromString(url));
720
		    article.setUri(null);
721
		}
722
		return (Reference)article;
723
	}
724

    
725
	private Reference makePartOfOtherTitle (Map<String, Object> valueMap){
726

    
727
		Reference result;
728
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
729
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
730
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
731

    
732
		if (inRefCategoryFk == null){
733
			//null -> error
734
			logger.warn("Part-Of-Other-Title has no inRefCategoryFk! RefId = " + refId + ". ReferenceType set to Generic.");
735
			result = makeUnknown(valueMap);
736
		}else if (inRefFk == null){
737
			//TODO is this correct ??
738
			logger.warn("Part-Of-Other-Title has no in reference: " + refId);
739
			result = makeUnknown(valueMap);
740
		}else if (inRefCategoryFk == REF_BOOK){
741
			//BookSection
742
			IBookSection bookSection = ReferenceFactory.newBookSection();
743
			result = (Reference)bookSection;
744
		}else if (inRefCategoryFk == REF_ARTICLE){
745
			//Article
746
			logger.info("Reference (refId = " + refId + ") of type 'part_of_other_title' is part of 'article'." +
747
					" We use the section reference type for such in references now.") ;
748
			result = ReferenceFactory.newSection();
749
		}else if (inRefCategoryFk == REF_JOURNAL){
750
			//TODO
751
			logger.warn("Reference (refId = " + refId + ") of type 'part_of_other_title' has inReference of type 'journal'." +
752
					" This is not allowed! Generic reference created instead") ;
753
			result = ReferenceFactory.newGeneric();
754
			result.addMarker(Marker.NewInstance(MarkerType.TO_BE_CHECKED(), true));
755
		}else if (inRefCategoryFk == REF_PART_OF_OTHER_TITLE){
756
			logger.info("Reference (refId = " + refId + ") of type 'part_of_other_title' has inReference 'part of other title'." +
757
					" This is allowed, but may be true only for specific cases (e.g. parts of book chapters). You may want to check if this is correct") ;
758
			result = ReferenceFactory.newSection();
759
		}else{
760
			logger.warn("InReference type (catFk = " + inRefCategoryFk + ") of part-of-reference not recognized for refId " + refId + "." +
761
				" Create 'Generic' reference instead");
762
			result = ReferenceFactory.newGeneric();
763
		}
764
		makeStandardMapper(valueMap, result); //url, pages
765
		return result;
766
	}
767

    
768

    
769
	/**
770
	 * @param inRefFkInt
771
	 * @param biblioRefToSave
772
	 * @param nomRefToSave
773
	 * @param relatedBiblioReferences
774
	 * @param relatedNomReferences
775
	 * @return
776
	 */
777
	private boolean existsInMapOrToSave(Integer inRefFkInt, Map<Integer, Reference> refToSave, Map<String, Reference> relatedReferences) {
778
		boolean result = false;
779
		if (inRefFkInt == null){
780
			return false;
781
		}
782
		result |= refToSave.containsKey(inRefFkInt);
783
		result |= relatedReferences.containsKey(String.valueOf(inRefFkInt));
784
		return result;
785
	}
786

    
787
	private Reference makeWebSite(Map<String, Object> valueMap){
788
		if (logger.isDebugEnabled()){logger.debug("RefType 'Website'");}
789
		Reference webPage = ReferenceFactory.newWebPage();
790
		makeStandardMapper(valueMap, webPage); //placePublished, publisher
791
		return webPage;
792
	}
793

    
794
	private Reference makeUnknown(Map<String, Object> valueMap){
795
		if (logger.isDebugEnabled()){logger.debug("RefType 'Unknown'");}
796
		Reference generic = ReferenceFactory.newGeneric();
797
//		generic.setSeries(series);
798
		makeStandardMapper(valueMap, generic); //pages, placePublished, publisher, series, volume
799
		return generic;
800
	}
801

    
802
	private Reference makeInformal(Map<String, Object> valueMap){
803
		if (logger.isDebugEnabled()){logger.debug("RefType 'Informal'");}
804
		Reference generic = ReferenceFactory.newGeneric();
805
//		informal.setSeries(series);
806
		makeStandardMapper(valueMap, generic);//editor, pages, placePublished, publisher, series, volume
807
		String informal = (String)valueMap.get("InformalRefCategory".toLowerCase());
808
		if (isNotBlank(informal) ){
809
			generic.addExtension(informal, ExtensionType.INFORMAL_CATEGORY());
810
		}
811
		return generic;
812
	}
813

    
814
	private Reference makeDatabase(Map<String, Object> valueMap){
815
		if (logger.isDebugEnabled()){logger.debug("RefType 'Database'");}
816
		Reference database =  ReferenceFactory.newDatabase();
817
		makeStandardMapper(valueMap, database); //?
818
		return database;
819
	}
820

    
821
	private Reference makeJournal(Map<String, Object> valueMap){
822
		if (logger.isDebugEnabled()){logger.debug("RefType 'Journal'");}
823
		Reference journal = ReferenceFactory.newJournal();
824

    
825
		Set<String> omitAttributes = new HashSet<>();
826
		String series = "series";
827
//		omitAttributes.add(series);
828

    
829
		makeStandardMapper(valueMap, journal, omitAttributes); //issn,placePublished,publisher
830
//		if (valueMap.get(series) != null){
831
//			logger.warn("Series not yet implemented for journal!");
832
//		}
833
		return journal;
834
	}
835

    
836
	private Reference makeBook(
837
				Map<String, Object> valueMap){
838

    
839
		if (logger.isDebugEnabled()){logger.debug("RefType 'Book'");}
840
		Reference book = ReferenceFactory.newBook();
841
//		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
842

    
843
		//Set bookAttributes = new String[]{"edition", "isbn", "pages","publicationTown","publisher","volume"};
844

    
845
		Set<String> omitAttributes = new HashSet<>();
846
		String attrSeries = "series";
847
//		omitAttributes.add(attrSeries);
848

    
849
		makeStandardMapper(valueMap, book, omitAttributes);
850

    
851
		//Series (as String)
852
		IPrintSeries printSeries = null;
853
		if (valueMap.get(attrSeries) != null){
854
			String series = (String)valueMap.get("title".toLowerCase());
855
			if (series == null){
856
				String nomTitle = (String)valueMap.get("nomTitleAbbrev".toLowerCase());
857
				series = nomTitle;
858
			}
859
			printSeries = ReferenceFactory.newPrintSeries(series);
860
			logger.info("Implementation of printSeries is preliminary");
861
		}
862
		//Series (as Reference)
863
		if (book.getInSeries() != null && printSeries != null){
864
			logger.warn("Book has series string and inSeries reference. Can not take both. Series string neglected");
865
		}else{
866
			book.setInSeries(printSeries);
867
		}
868
		book.setEditor(null);
869

    
870
		return book;
871

    
872
	}
873

    
874

    
875
	int parsedBookVolumes = 0;
876
    private void extraktBookVolume(Reference book) {
877
        if (isExtractBookVolumeCandidate(book)){
878
            String patternStr = "(.{2,})\\s(\\d{1,2})";
879
            int groupIndex = 2;
880
            Pattern pattern = Pattern.compile(patternStr);
881

    
882
            String abbrevCache = book.getAbbrevTitleCache();
883
            String titleCache = book.getTitleCache();
884
            String vol = null;
885
            String volFull = null;
886
            String abbrev = book.getAbbrevTitle();
887
            if (isNotBlank(abbrev)){
888
                Matcher matcher = pattern.matcher(abbrev);
889
                if (matcher.matches()){
890
                    vol = matcher.group(groupIndex);
891
                    abbrev = matcher.group(1);
892
                }
893
            }
894

    
895
            String full = book.getTitle();
896
            if (isNotBlank(full)){
897
                Matcher matcher = pattern.matcher(full);
898
                if (matcher.matches()){
899
                    volFull = matcher.group(groupIndex);
900
                    full = matcher.group(1);
901
                }
902
            }
903
            if (vol != null && volFull != null){
904
                if (!vol.equals(volFull)){
905
                    return;
906
                }
907
            }else if (vol == null && volFull == null){
908
                return;
909
            }else if (vol == null){
910
                if (isNotBlank(abbrev)){
911
                    return;
912
                }else{
913
                    vol = volFull;
914
                }
915
            }else if (volFull == null){
916
                if (isNotBlank(full)){
917
                    return;
918
                }
919
            }else{
920
                logger.warn("Should not happen");
921
            }
922
            book.setVolume(vol);
923
            book.setAbbrevTitle(abbrev);
924
            book.setTitle(full);
925
            if (!book.getAbbrevTitleCache().equals(abbrevCache)){
926
                logger.warn("Abbrev title cache for parsed book volume does not match: " + book.getAbbrevTitleCache() + " <-> "+abbrevCache);
927
            }else if (!book.getTitleCache().equals(titleCache)){
928
                logger.warn("Title cache for parsed book volume does not match: " + book.getTitleCache() + " <-> "+titleCache);
929
            }else{
930
//                System.out.println(titleCache);
931
//                System.out.println(abbrevCache);
932
                parsedBookVolumes++;
933
            }
934
        }else{
935
            return;
936
        }
937
    }
938

    
939
    /**
940
     * @param book
941
     * @return
942
     */
943
    private boolean isExtractBookVolumeCandidate(Reference book) {
944
        if (isNotBlank(book.getVolume()) || isNotBlank(book.getEdition()) || isNotBlank(book.getSeriesPart())){
945
            return false;
946
        }
947
        if (!checkExtractBookVolumeTitle(book.getAbbrevTitle())){
948
            return false;
949
        }
950
        if (!checkExtractBookVolumeTitle(book.getTitle())){
951
            return false;
952
        }
953
        return true;
954
    }
955

    
956
    /**
957
     * @param abbrevTitle
958
     * @return
959
     */
960
    private boolean checkExtractBookVolumeTitle(String title) {
961
        if (title == null){
962
            return true;
963
        }
964
        if (title.contains(",") || title.contains("ed.") || title.contains("Ed.")|| title.contains("Suppl")
965
                || title.contains("Ser.")|| title.contains("ser.")) {
966
            return false;
967
        }
968
        return true;
969
    }
970

    
971
    /**
972
	 * Returns the requested object if it exists in one of both maps. Prefers the refToSaveMap in ambigious cases.
973
	 * @param inRefFkInt
974
	 * @param nomRefToSave
975
	 * @param relatedNomReferences
976
	 * @return
977
	 */
978
	private Reference getReferenceFromMaps(
979
			int inRefFkInt,
980
			Map<Integer, Reference> refToSaveMap,
981
			Map<String, Reference> relatedRefMap) {
982
		Reference result = null;
983
		result = refToSaveMap.get(inRefFkInt);
984
		if (result == null){
985
			result = relatedRefMap.get(String.valueOf(inRefFkInt));
986
		}
987
		return result;
988
	}
989

    
990
	private Reference makePrintSeries(Map<String, Object> valueMap){
991
		if (logger.isDebugEnabled()){logger.debug("RefType 'PrintSeries'");}
992
		Reference printSeries = ReferenceFactory.newPrintSeries();
993
		makeStandardMapper(valueMap, printSeries, null);
994
		return printSeries;
995
	}
996

    
997
	private Reference makeProceedings(Map<String, Object> valueMap){
998
		if (logger.isDebugEnabled()){logger.debug("RefType 'Proceedings'");}
999
		Reference proceedings = ReferenceFactory.newProceedings();
1000
		makeStandardMapper(valueMap, proceedings, null);
1001
		return proceedings;
1002
	}
1003

    
1004
	private Reference makeThesis(Map<String, Object> valueMap){
1005
		if (logger.isDebugEnabled()){logger.debug("RefType 'Thesis'");}
1006
		Reference thesis = ReferenceFactory.newThesis();
1007
		makeStandardMapper(valueMap, thesis, null);
1008
		return thesis;
1009
	}
1010

    
1011

    
1012
	private Reference makeJournalVolume(Map<String, Object> valueMap){
1013
		if (logger.isDebugEnabled()){logger.debug("RefType 'JournalVolume'");}
1014
		//Proceedings proceedings = Proceedings.NewInstance();
1015
		Reference journalVolume = ReferenceFactory.newGeneric();
1016
		makeStandardMapper(valueMap, journalVolume, null);
1017
		logger.warn("Journal volumes not yet implemented. Generic created instead but with errors");
1018
		return journalVolume;
1019
	}
1020

    
1021
	private boolean makeStandardMapper(Map<String, Object> valueMap, Reference ref){
1022
		return makeStandardMapper(valueMap, ref, null);
1023
	}
1024

    
1025

    
1026
	private boolean makeStandardMapper(Map<String, Object> valueMap, CdmBase cdmBase, Set<String> omitAttributes){
1027
		boolean result = true;
1028
		for (CdmAttributeMapperBase mapper : classMappers){
1029
			if (mapper instanceof CdmSingleAttributeMapperBase){
1030
				result &= makeStandardSingleMapper(valueMap, cdmBase, (CdmSingleAttributeMapperBase)mapper, omitAttributes);
1031
			}else if (mapper instanceof CdmOneToManyMapper){
1032
				result &= makeMultipleValueAddMapper(valueMap, cdmBase, (CdmOneToManyMapper)mapper, omitAttributes);
1033
			}else{
1034
				logger.error("Unknown mapper type");
1035
				result = false;
1036
			}
1037
		}
1038
		return result;
1039
	}
1040

    
1041
	private boolean makeStandardSingleMapper(Map<String, Object> valueMap, CdmBase cdmBase, CdmSingleAttributeMapperBase mapper, Set<String> omitAttributes){
1042
		boolean result = true;
1043
		if (omitAttributes == null){
1044
			omitAttributes = new HashSet<>();
1045
		}
1046
		if (mapper instanceof DbImportExtensionMapper){
1047
			result &= ((DbImportExtensionMapper)mapper).invoke(valueMap, cdmBase);
1048
		}else if (mapper instanceof DbImportMarkerMapper){
1049
			result &= ((DbImportMarkerMapper)mapper).invoke(valueMap, cdmBase);
1050
		}else{
1051
			String sourceAttribute = mapper.getSourceAttributeList().get(0).toLowerCase();
1052
			Object value = valueMap.get(sourceAttribute);
1053
			if (mapper instanceof CdmUriMapper && value != null){
1054
				try {
1055
					value = new URI (value.toString());
1056
				} catch (URISyntaxException e) {
1057
					logger.error("URI syntax exception: " + value.toString());
1058
					value = null;
1059
				}
1060
			}
1061
			if (mapper instanceof CdmUuidMapper && value != null){
1062
                try {
1063
                    value = UUID.fromString(value.toString());
1064
                } catch (IllegalArgumentException e) {
1065
                    logger.error("UUID syntax exception: " + value.toString());
1066
                    value = null;
1067
                }
1068
            }
1069
			if (value != null){
1070
				String destinationAttribute = mapper.getDestinationAttribute();
1071
				if (! omitAttributes.contains(destinationAttribute)){
1072
					result &= ImportHelper.addValue(value, cdmBase, destinationAttribute, mapper.getTypeClass(), OVERWRITE, OBLIGATORY);
1073
				}
1074
			}
1075
		}
1076
		return result;
1077
	}
1078

    
1079

    
1080
	private boolean makeMultipleValueAddMapper(Map<String, Object> valueMap, CdmBase cdmBase, CdmOneToManyMapper<CdmBase, CdmBase, CdmSingleAttributeMapperBase> mapper, Set<String> omitAttributes){
1081
		if (omitAttributes == null){
1082
			omitAttributes = new HashSet<>();
1083
		}
1084
		boolean result = true;
1085
		String destinationAttribute = mapper.getSingleAttributeName();
1086
		List<Object> sourceValues = new ArrayList<>();
1087
		List<Class> classes = new ArrayList<>();
1088
		for (CdmSingleAttributeMapperBase singleMapper : mapper.getSingleMappers()){
1089
			String sourceAttribute = singleMapper.getSourceAttribute();
1090
			Object value = valueMap.get(sourceAttribute);
1091
			sourceValues.add(value);
1092
			Class<?> clazz = singleMapper.getTypeClass();
1093
			classes.add(clazz);
1094
		}
1095

    
1096
		result &= ImportHelper.addMultipleValues(sourceValues, cdmBase, destinationAttribute, classes, NO_OVERWRITE, OBLIGATORY);
1097
		return result;
1098
	}
1099

    
1100

    
1101
	private TeamOrPersonBase<?> getAuthorship(BerlinModelImportState state, String refAuthorString,
1102
	        TeamOrPersonBase<?> nomAuthor, Integer refId){
1103

    
1104
	    TeamOrPersonBase<?> result;
1105
		if (nomAuthor != null){
1106
			result = nomAuthor;
1107
			if (isNotBlank(refAuthorString) && !nomAuthor.getTitleCache().equals(refAuthorString)){
1108
			    boolean isSimilar = handleSimilarAuthors(state, refAuthorString, nomAuthor, refId);
1109
			    if (! isSimilar){
1110
			        String message = "refAuthorString differs from nomAuthor.titleCache: " + refAuthorString
1111
                            + " <-> " + nomAuthor.getTitleCache() + "; RefId: " + refId;
1112
			        logger.warn(message);
1113
			    }
1114
			}
1115
		} else if (isNotBlank(refAuthorString)){//only RefAuthorString exists
1116
		    refAuthorString = refAuthorString.trim();
1117
			//TODO match with existing Persons/Teams
1118
		    TeamOrPersonBase<?> author = state.getRelatedObject(REF_AUTHOR_NAMESPACE, refAuthorString, TeamOrPersonBase.class);
1119
			if (author == null){
1120
			    if (!BerlinModelAuthorTeamImport.hasTeamSeparator(refAuthorString)){
1121
			        author = makePerson(refAuthorString, false, refId);
1122
			    }else{
1123
			        author = makeTeam(state, refAuthorString, refId);
1124
			    }
1125
			    state.addRelatedObject(REF_AUTHOR_NAMESPACE, refAuthorString, author);
1126
			    result = deduplicatePersonOrTeam(state, author);
1127

    
1128
			    if (result != author){
1129
                    logger.debug("RefAuthorString author deduplicated " + author);
1130
                }else{
1131
                    if (!importSourceExists(author, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference() )){
1132
                        author.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1133
                    }
1134
                }
1135
			}else{
1136
			    logger.debug("RefAuthor loaded from map");
1137
			}
1138
			result = author;
1139
		}else{
1140
			result = null;
1141
		}
1142

    
1143
		return result;
1144
	}
1145

    
1146

    
1147
    /**
1148
     * @param state
1149
     * @param refAuthorString
1150
     * @param refId
1151
     * @return
1152
     */
1153
    private TeamOrPersonBase<?> makeTeam(BerlinModelImportState state, String refAuthorString, Integer refId) {
1154
        Team team = Team.NewInstance();
1155
        boolean hasDedupMember = false;
1156
        if (containsEdOrColon(refAuthorString)){
1157
            team.setTitleCache(refAuthorString, true);
1158
        }else{
1159
            String[] refAuthorTeams = BerlinModelAuthorTeamImport.splitTeam(refAuthorString);
1160
            boolean lastWasInitials = false;
1161
            for (int i = 0; i< refAuthorTeams.length ;i++){
1162
                if (lastWasInitials){
1163
                    lastWasInitials = false;
1164
                    continue;
1165
                }
1166
                String fullTeam = refAuthorTeams[i].trim();
1167
                String initials = null;
1168
                if (refAuthorTeams.length > i+1){
1169
                    String nextSplit = refAuthorTeams[i+1].trim();
1170
                    if (isInitial(nextSplit)){
1171
                        lastWasInitials = true;
1172
                        initials = nextSplit;
1173
                    }
1174
                }
1175
                Person member = makePerson(fullTeam, isNotBlank(initials), refId);
1176

    
1177
                if (initials != null){
1178
                    if (member.getInitials() != null){
1179
                        logger.warn("Initials already set: " + refId);
1180
                    }else if (!member.isProtectedTitleCache()){
1181
                        member.setInitials(initials);
1182
                    }else {
1183
                        member.setTitleCache(member.getTitleCache() + ", " + initials, true);
1184
                    }
1185
                }
1186

    
1187
                if (i == refAuthorTeams.length -1 && BerlinModelAuthorTeamImport.isEtAl(member)){
1188
                    team.setHasMoreMembers(true);
1189
                }else{
1190
                    Person dedupMember = deduplicatePersonOrTeam(state, member);
1191
                    if (dedupMember != member){
1192
                        hasDedupMember = true;
1193
                    }else{
1194
                        if (!importSourceExists(member, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference())){
1195
                            member.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1196
                        }
1197
                    }
1198

    
1199
                    team.addTeamMember(dedupMember);
1200
                }
1201
            }
1202
        }
1203

    
1204
        TeamOrPersonBase<?> result = team;
1205
        if (team.getTeamMembers().size() == 1 && !team.isHasMoreMembers()){
1206
            Person person = team.getTeamMembers().get(0);
1207
            checkPerson(person, refAuthorString, hasDedupMember, refId);
1208
            result = person;
1209
        }else{
1210
            checkTeam(team, refAuthorString, refId);
1211
            result = team;
1212
        }
1213

    
1214
        return result;
1215
    }
1216

    
1217
    private static void checkTeam(Team team, String refAuthorString, Integer refId) {
1218
        TeamDefaultCacheStrategy formatter = (TeamDefaultCacheStrategy) team.cacheStrategy();
1219

    
1220
        if (formatter.getTitleCache(team).equals(refAuthorString)){
1221
            team.setProtectedTitleCache(false);
1222
        }else if(formatter.getTitleCache(team).replace(" & ", ", ").equals(refAuthorString.replace(" & ", ", ").replace(" ,", ","))){
1223
            //also accept teams with ', ' as final member separator as not protected
1224
            team.setProtectedTitleCache(false);
1225
        }else if(formatter.getFullTitle(team).replace(" & ", ", ").equals(refAuthorString.replace(" & ", ", "))){
1226
            //.. or teams with initials first
1227
            team.setProtectedTitleCache(false);
1228
        }else if (containsEdOrColon(refAuthorString)){
1229
            //nothing to do, it is expected to be protected
1230

    
1231
        }else{
1232
            team.setTitleCache(refAuthorString, true);
1233
            logger.warn("Creation of titleCache for team with members did not (fully) work: " + refAuthorString + " <-> " + formatter.getTitleCache(team)+ " : " + refId);
1234
        }
1235
    }
1236

    
1237
    private static void checkPerson(Person person, String refAuthorString, boolean hasDedupMember, Integer refId) {
1238
        PersonDefaultCacheStrategy formatter = (PersonDefaultCacheStrategy) person.cacheStrategy();
1239

    
1240
        String oldTitleCache = person.getTitleCache();
1241
        boolean oldTitleCacheProtected = person.isProtectedTitleCache();
1242

    
1243
        if (! oldTitleCache.equals(refAuthorString)){
1244
            logger.error("Old titleCache does not equal refAuthorString this should not happen. "+ oldTitleCache + " <-> " + refAuthorString + "; refId = " + refId);
1245
        }
1246

    
1247
        boolean protect = true;
1248
        person.setProtectedTitleCache(false);
1249
        if (refAuthorString.equals(formatter.getTitleCache(person))){
1250
            protect = false;
1251
        }else if(formatter.getFullTitle(person).equals(refAuthorString)){
1252
            //.. or teams with initials first
1253
            protect = false;
1254
        }else{
1255
            //keep protected, see below
1256
        }
1257

    
1258
        if (hasDedupMember){
1259
            //restore
1260
            //TODO maybe even do not use dedup for testing
1261
            person.setTitleCache(oldTitleCache, oldTitleCacheProtected);
1262
            if (protect != oldTitleCacheProtected){
1263
                logger.warn("Deduplicated person protection requirement unclear for "+refAuthorString+". New:"+protect+"/Old:"+oldTitleCacheProtected+"; RefId: " + refId);
1264
            }
1265
        }else{
1266
            if (protect){
1267
                logger.warn("Creation of titleCache for person (converted from team) with members did not (fully) work: " + refAuthorString + " <-> " + formatter.getTitleCache(person)+ " : " + refId);
1268
                person.setTitleCache(refAuthorString, protect);
1269
            }else{
1270
                //keep unprotected
1271
            }
1272
        }
1273
    }
1274

    
1275
    private static boolean containsEdOrColon(String str) {
1276
        if (str.contains(" ed.") || str.contains(" Ed.") || str.contains("(ed.")
1277
                || str.contains("[ed.") || str.contains("(Eds)") || str.contains("(Eds.)") ||
1278
                str.contains("(eds.)") || str.contains(":")|| str.contains(";") || str.contains("Publ. & Inform. Directorate")
1279
                || str.contains("Anonymous [Department of Botany, Faculty of Science, FER-ZPR, University of Zagreb]")
1280
                || str.contains("Davis, P. H. (Güner, A. & al.)")){
1281
            return true;
1282
        }else{
1283
            return false;
1284
        }
1285
    }
1286

    
1287
    /**
1288
     * @param nextSplit
1289
     * @return
1290
     */
1291
    private static boolean isInitial(String str) {
1292
        if (str == null){
1293
            return false;
1294
        }
1295
        boolean matches = str.trim().matches("(\\p{javaUpperCase}|Yu|Ya|Th|Ch|Lj|Sz|Dz|Sh|Ju|R. M. da S)\\.?"
1296
                + "(\\s*[-\\s]\\s*(\\p{javaUpperCase}|Yu|Ja|Kh|Tz|Ya|Th|Ju)\\.?)*(\\s+(van|von|de|de la|del|da|van der))?");
1297
        return matches;
1298
    }
1299

    
1300
    private <T extends TeamOrPersonBase<?>> T deduplicatePersonOrTeam(BerlinModelImportState state,T author) {
1301
        T result = state.getDeduplicationHelper().getExistingAuthor(author, true);
1302
        return result;
1303
    }
1304

    
1305
    private Reference deduplicateReference(BerlinModelImportState state,Reference ref) {
1306
        Reference result = state.getDeduplicationHelper().getExistingReference(ref, true);
1307
        return result;
1308
    }
1309

    
1310
    private static Person makePerson(String full, boolean followedByInitial, Integer refId) {
1311
        Person person = Person.NewInstance();
1312
        person.setTitleCache(full, true);
1313
        if (!full.matches(".*[\\s\\.].*")){
1314
            person.setFamilyName(full);
1315
            person.setProtectedTitleCache(false);
1316
        }else{
1317
            parsePerson(person, full, true, followedByInitial);
1318
        }
1319

    
1320
        if ((full.length() <= 2 && !full.matches("(Li|Bo|Em|Ay|Ma)")) || (full.length() == 3 && full.endsWith(".") && !full.equals("al.")) ){
1321
            logger.warn("Unexpected short nom author name part: " + full + "; " + refId);
1322
        }
1323

    
1324
        return person;
1325
    }
1326

    
1327
    private static void parsePerson(Person person, String str, boolean preliminary, boolean followedByInitial) {
1328
        String capWord = "\\p{javaUpperCase}\\p{javaLowerCase}{2,}";
1329
        String famStart = "(Le |D'|'t |Mc|Mac|Des |d'|Du |De |Al-)";
1330
        String regEx = "((\\p{javaUpperCase}|Ya|Th|Ju|Kh|An)\\.([\\s-]\\p{javaUpperCase}\\.)*(\\s(de|del|da|von|van|van der|v.|af|zu|von M. Und L.))?\\s)("
1331
                + famStart + "?" + capWord + "((-| y | i | é | de | de la )" + capWord + ")?)";
1332
        Matcher matcher = Pattern.compile(regEx).matcher(str);
1333
        if (matcher.matches()){
1334
            person.setProtectedTitleCache(false);
1335
            String familyName = matcher.group(6).trim();
1336
            person.setFamilyName(familyName);
1337
            person.setInitials(matcher.group(1).trim());
1338
        }else{
1339
            String regEx2 = "("+ capWord + "\\s" + capWord + "|Le Sueur|Beck von Mannagetta|Di Martino|Galán de Mera|Van Der Maesen|Farga i Arquimbau|Perez de Paz|Borzatti de Loewenstern|Lo Giudice|Perez de Paz)";
1340
            Matcher matcher2 = Pattern.compile(regEx2).matcher(str);
1341
            if (followedByInitial && matcher2.matches()){
1342
                person.setFamilyName(str);
1343
                person.setProtectedTitleCache(false);
1344
            }else{
1345
                person.setTitleCache(str, preliminary);
1346
            }
1347
        }
1348
    }
1349

    
1350
    private static boolean handleSimilarAuthors(BerlinModelImportState state, String refAuthorString,
1351
            TeamOrPersonBase<?> nomAuthor, int refId) {
1352
        String nomTitle = nomAuthor.getTitleCache();
1353

    
1354
        if (refAuthorString.equals(nomAuthor.getNomenclaturalTitleCache())){
1355
            //nomTitle equal
1356
            return true;
1357
        }else{
1358
            if (refAuthorString.replace(" & ", ", ").equals(nomTitle.replace(" & ", ", "))){
1359
                //nomTitle equal except for "&"
1360
                return true;
1361
            }
1362
            String nomFullTitle = nomAuthor.getFullTitle();
1363
            if (refAuthorString.replace(" & ", ", ").equals(nomFullTitle.replace(" & ", ", "))){
1364
                return true;
1365
            }
1366

    
1367
            if (nomAuthor.isInstanceOf(Person.class)){
1368
                Person person = CdmBase.deproxy(nomAuthor, Person.class);
1369

    
1370
                //refAuthor has initials behind, nom Author in front // the other way round is handled in firstIsFullNameOfInitialName
1371
                if (refAuthorString.contains(",") && !nomTitle.contains(",") ){
1372
                    String[] splits = refAuthorString.split(",");
1373
                    if (splits.length == 2){
1374
                        String newMatch = splits[1].trim() + " " + splits[0].trim();
1375
                        if (newMatch.equals(nomTitle)){
1376
                            if (isBlank(person.getFamilyName())){
1377
                                person.setFamilyName(splits[0].trim());
1378
                            }
1379
                            if (isBlank(person.getInitials())){
1380
                                person.setInitials(splits[1].trim());
1381
                            }
1382
                            return true;
1383
                        }
1384
                    }
1385
                }
1386

    
1387
                if (refAuthorIsFamilyAuthorOfNomAuthor(state, refAuthorString, person)){
1388
                    return true;
1389
                }
1390

    
1391
                if (firstIsFullNameOfInitialName(state, refAuthorString, person, refId)){
1392
                    return true;
1393
                }
1394
            }
1395

    
1396
        }
1397
        return false;
1398
    }
1399

    
1400
    /**
1401
     * @param state
1402
     * @param refAuthorString
1403
     * @param person
1404
     * @return
1405
     */
1406
    private static boolean refAuthorIsFamilyAuthorOfNomAuthor(BerlinModelImportState state, String refAuthorString,
1407
            Person person) {
1408
        if (refAuthorString.equals(person.getFamilyName())){
1409
            return true;
1410
        }else{
1411
            return false;
1412
        }
1413
    }
1414

    
1415
    /**
1416
     * @param state
1417
     * @param refAuthorString
1418
     * @param nomAuthor
1419
     * @return
1420
     */
1421
    private static boolean firstIsFullNameOfInitialName(BerlinModelImportState state, String fullName,
1422
            Person initialAuthor, int refId) {
1423
        String initialName = initialAuthor.getTitleCache();
1424

    
1425
        String[] fullSplits = fullName.split(",");
1426
        String[] initialSplits = initialName.split(",");
1427

    
1428
        if (fullSplits.length == 2 && initialSplits.length == 2){
1429
            String[] fullGivenName = fullSplits[1].trim().split(" ");
1430
            String[] initialsGivenName = initialSplits[1].trim().split(" ");
1431
            boolean result = compareFamilyAndInitials(fullSplits[0], initialSplits[0], fullGivenName, initialsGivenName);
1432
            if (result){
1433
                setGivenName(state, fullSplits[1], initialAuthor, refId);
1434
            }
1435
            return result;
1436
        }else if (fullSplits.length == 1 && initialSplits.length == 2){
1437
            String[] fullSingleSplits = fullName.split(" ");
1438
            String fullFamily = fullSingleSplits[fullSingleSplits.length-1];
1439
            String[] fullGivenName = Arrays.copyOfRange(fullSingleSplits, 0, fullSingleSplits.length-1);
1440
            String[] initialsGivenName = initialSplits[1].trim().split(" ");
1441
            boolean result =  compareFamilyAndInitials(fullFamily, initialSplits[0], fullGivenName, initialsGivenName);
1442
            if (result){
1443
                if(hasAtLeastOneFullName(fullGivenName)){
1444
                    setGivenName(state, CdmUtils.concat(" ", fullGivenName), initialAuthor, refId);
1445
                }
1446
            }
1447
            return result;
1448
        }else if (fullSplits.length == 1 && initialAuthor.getInitials() == null){
1449
            //don't if this will be implemented, initialAuthors with only nomencl.Author set
1450
        }
1451

    
1452
        return false;
1453
    }
1454

    
1455
    /**
1456
     * @param fullGivenName
1457
     * @return
1458
     */
1459
    private static boolean hasAtLeastOneFullName(String[] fullGivenName) {
1460
        for (String singleName : fullGivenName){
1461
            if (!singleName.endsWith(".") && singleName.length() > 2 && !singleName.matches("(von|van)") ){
1462
                return true;
1463
            }
1464
        }
1465
        return false;
1466
    }
1467

    
1468
    private static void setGivenName(BerlinModelImportState state, String givenName, Person person, int refId) {
1469
        givenName = givenName.trim();
1470
        if(person.getGivenName() == null || person.getGivenName().equals(givenName)){
1471
            person.setGivenName(givenName);
1472
        }else{
1473
            logger.warn("RefAuthor given name and existing given name differ: " + givenName + " <-> " + person.getGivenName() + "; RefId + " + refId);
1474
        }
1475
    }
1476

    
1477
    protected static boolean compareFamilyAndInitials(String fullFamilyName, String initialsFamilyName,
1478
            String[] fullGivenName, String[] initialsGivenName) {
1479
        if (!fullFamilyName.equals(initialsFamilyName)){
1480
            return false;
1481
        }
1482
        if (fullGivenName.length == initialsGivenName.length){
1483
            for (int i =0; i< fullGivenName.length ; i++){
1484
                if (fullGivenName[i].length() == 0  //comma ending not allowed
1485
                        || initialsGivenName[i].length() != 2 //only K. or similar allowed
1486
                        || fullGivenName[i].length() < initialsGivenName[i].length()  //fullFirstName must be longer than abbrev Name
1487
                        || !initialsGivenName[i].endsWith(".") //initials must end with "."
1488
                        || !fullGivenName[i].startsWith(initialsGivenName[i].replace(".", ""))){ //start with same letter
1489
                    if (fullGivenName[i].matches("(von|van|de|zu)") && fullGivenName[i].equals(initialsGivenName[i])){
1490
                        continue;
1491
                    }else{
1492
                        return false;
1493
                    }
1494
                }
1495
            }
1496
            return true;
1497
        }else{
1498
            return false;
1499
        }
1500
    }
1501

    
1502
	public Set<String> getObligatoryAttributes(boolean lowerCase, BerlinModelImportConfigurator config){
1503
		Set<String> result = new HashSet<>();
1504
		Class<ICdmImport>[] ioClassList = config.getIoClassList();
1505
		result.addAll(Arrays.asList(unclearMappers));
1506
		result.addAll(Arrays.asList(createdAndNotesAttributes));
1507
		result.addAll(Arrays.asList(operationalAttributes));
1508
		CdmIoMapping mapping = new CdmIoMapping();
1509
		for (CdmAttributeMapperBase mapper : classMappers){
1510
			mapping.addMapper(mapper);
1511
		}
1512
		result.addAll(mapping.getSourceAttributes());
1513
		if (lowerCase){
1514
			Set<String> lowerCaseResult = new HashSet<>();
1515
			for (String str : result){
1516
				if (str != null){lowerCaseResult.add(str.toLowerCase());}
1517
			}
1518
			result = lowerCaseResult;
1519
		}
1520
		return result;
1521
	}
1522

    
1523
	@Override
1524
	protected boolean doCheck(BerlinModelImportState state){
1525
		BerlinModelReferenceImportValidator validator = new BerlinModelReferenceImportValidator();
1526
		return validator.validate(state, this);
1527
	}
1528

    
1529
	@Override
1530
	protected boolean isIgnore(BerlinModelImportState state){
1531
		return (state.getConfig().getDoReferences() == IImportConfigurator.DO_REFERENCES.NONE);
1532
	}
1533

    
1534
}
(14-14/22)