Project

General

Profile

Download (59.6 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 static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_ARTICLE;
13
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_BOOK;
14
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_CONFERENCE_PROCEEDINGS;
15
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_DATABASE;
16
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_INFORMAL;
17
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_JOURNAL;
18
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_JOURNAL_VOLUME;
19
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_PART_OF_OTHER_TITLE;
20
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_PRINT_SERIES;
21
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_UNKNOWN;
22
import static eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer.REF_WEBSITE;
23
import static eu.etaxonomy.cdm.io.common.ImportHelper.NO_OVERWRITE;
24
import static eu.etaxonomy.cdm.io.common.ImportHelper.OBLIGATORY;
25
import static eu.etaxonomy.cdm.io.common.ImportHelper.OVERWRITE;
26

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

    
42
import org.apache.log4j.Logger;
43
import org.springframework.stereotype.Component;
44

    
45
import eu.etaxonomy.cdm.common.CdmUtils;
46
import eu.etaxonomy.cdm.common.DOI;
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.utils.ImportDeduplicationHelper;
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.DefinedTerm;
69
import eu.etaxonomy.cdm.model.common.ExtensionType;
70
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
71
import eu.etaxonomy.cdm.model.common.Identifier;
72
import eu.etaxonomy.cdm.model.common.Marker;
73
import eu.etaxonomy.cdm.model.common.MarkerType;
74
import eu.etaxonomy.cdm.model.common.TermVocabulary;
75
import eu.etaxonomy.cdm.model.reference.IArticle;
76
import eu.etaxonomy.cdm.model.reference.IBookSection;
77
import eu.etaxonomy.cdm.model.reference.IPrintSeries;
78
import eu.etaxonomy.cdm.model.reference.Reference;
79
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
80
import eu.etaxonomy.cdm.model.reference.ReferenceType;
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
    private static final long serialVersionUID = -3667566958769967591L;
91

    
92
    private static final Logger logger = Logger.getLogger(BerlinModelReferenceImport.class);
93

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

    
97

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

    
103
	private static ImportDeduplicationHelper<BerlinModelImportState> deduplicationHelper;
104

    
105
	private final int modCount = 1000;
106
	private static final String pluralString = "references";
107
	private static final String dbTableName = "reference";
108

    
109

    
110
	public BerlinModelReferenceImport(){
111
		super(dbTableName, pluralString);
112
	}
113

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

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

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

    
157

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

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

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

    
172
	//TODO isPaper
173
	//
174

    
175

    
176

    
177
	//type to count the references nomReferences that have been created and saved
178
	private class RefCounter{
179
		RefCounter() {refCount = 0;}
180
		int refCount;
181
		int dedupCount;
182

    
183
		@Override
184
        public String toString(){return String.valueOf(refCount) + "/" + String.valueOf(dedupCount) ;}
185
	}
186

    
187
	@Override
188
	protected String getRecordQuery(BerlinModelImportConfigurator config) {
189
		return null;  //not needed
190
	}
191

    
192
	@Override
193
	protected void doInvoke(BerlinModelImportState state){
194
		logger.info("start make " + getPluralString() + " ...");
195
		deduplicationHelper = ImportDeduplicationHelper.NewInstance(this, state);
196

    
197
		boolean success = true;
198
		initializeMappers(state);
199
		try {
200
            initializeCommonNameRefMap(state);
201
        } catch (SQLException e1) {
202
            e1.printStackTrace();
203
            logger.error("Error in initializeCommonNameRefMap in BerlinModelReferenceimport");
204
        }
205
		BerlinModelImportConfigurator config = state.getConfig();
206
		Source source = config.getSource();
207

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

    
219
		String referenceTable = CdmUtils.Nz(state.getConfig().getReferenceIdTable());
220
		referenceTable = referenceTable.isEmpty() ? " Reference"  : referenceTable + " as Reference ";
221
		String strIdFrom = String.format(strFrom, referenceTable );
222

    
223
		String referenceFilter = CdmUtils.Nz(state.getConfig().getReferenceIdTable());
224
		if (! referenceFilter.isEmpty()){
225
			referenceFilter = " AND " + referenceFilter + " ";
226
		}
227
		referenceFilter = "";  //don't use it for now, in E+M the tabelle is directly used
228

    
229
		String strIdQueryFirstPath = strSelectId + strIdFrom + strOrderBy ;
230
		String strIdQuerySecondPath = strSelectId + strIdFrom + " AND (Reference.InRefFk is NOT NULL) ";
231

    
232
//		if (config.getDoReferences() == CONCEPT_REFERENCES){
233
//			strIdQueryNoInRef += " AND ( Reference.refId IN ( SELECT ptRefFk FROM PTaxon) ) " + referenceFilter;
234
//		}
235

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

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

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

    
271
//	 not needed, data also in Reference.idInSource
272
//    private void fillSourceNumberMap(BerlinModelImportState state) throws SQLException {
273
//        String query = " SELECT * FROM SourceNumber2Ref ";
274
//        ResultSet rs = state.getConfig().getSource().getResultSet(query);
275
//        while (rs.next()){
276
//            String sourceNumber = rs.getString("SourceNumber");
277
//            int refId = rs.getInt("RefId");
278
//            if (isNotBlank(sourceNumber)){
279
//                String oldValue = sourceNumberMap.put(refId, sourceNumber.trim());
280
//                if (oldValue != null){
281
//                    logger.warn(">1 source number exists for refId: " + refId);
282
//                }
283
//            }
284
//        }
285
//    }
286

    
287
    @Override
288
	public boolean doPartition(@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner, BerlinModelImportState state) {
289
        deduplicationHelper.restartSession();
290

    
291
	    if (state.isReferenceSecondPath()){
292
			return doPartitionSecondPath(partitioner, state);
293
		}
294
		boolean success = true;
295

    
296
		Map<Integer, Reference> refToSave = new HashMap<>();
297

    
298
//		@SuppressWarnings("unchecked")
299
//        Map<String, Reference> relatedReferences = partitioner.getObjectMap(REFERENCE_NAMESPACE);
300

    
301
		BerlinModelImportConfigurator config = state.getConfig();
302

    
303
		try {
304

    
305
			int i = 0;
306
			RefCounter refCounter  = new RefCounter();
307
			ResultSet rs = partitioner.getResultSet();
308

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

    
313
				success &= makeSingleReferenceRecord(rs, state, partitioner, refToSave, refCounter);
314
			} // end resultSet
315

    
316
			//for the concept reference a fixed uuid may be needed -> change uuid
317
			Integer sourceSecId = (Integer)config.getSourceSecId();
318
			Reference sec = refToSave.get(sourceSecId);
319

    
320
			if (sec != null){
321
				sec.setUuid(config.getSecUuid());
322
				logger.info("SecUuid changed to: " + config.getSecUuid());
323
			}
324

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

    
328
			getReferenceService().saveOrUpdate(refToSave.values());
329

    
330
//			logger.info("end makeReferences ..." + getSuccessString(success));;
331
			return success;
332
		} catch (SQLException e) {
333
			logger.error("SQLException:" +  e);
334
			return false;
335
		}
336
	}
337

    
338

    
339

    
340
	/**
341
	 * Adds the inReference to the according references.
342
	 * @param partitioner
343
	 * @param state
344
	 * @return
345
	 */
346
	private boolean doPartitionSecondPath(@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner, BerlinModelImportState state) {
347
		boolean success = true;
348

    
349
		Map<Integer, Reference> refToSave = new HashMap<>();
350

    
351
		@SuppressWarnings("unchecked")
352
        Map<String, Reference> relatedReferencesMap = partitioner.getObjectMap(REFERENCE_NAMESPACE);
353

    
354
		try {
355
				int i = 0;
356
				RefCounter refCounter  = new RefCounter();
357

    
358
				ResultSet rs = partitioner.getResultSet();
359
				//for each resultset
360
				while (rs.next()){
361
					if ((i++ % modCount) == 0 && i!= 1 ){ logger.info("References handled: " + (i-1) + " in round -" );}
362

    
363
					Integer refId = rs.getInt("refId");
364
					Integer inRefFk = nullSafeInt(rs, "inRefFk");
365

    
366
					if (inRefFk != null){
367

    
368
						Reference thisRef = relatedReferencesMap.get(String.valueOf(refId));
369

    
370
						Reference inRef = relatedReferencesMap.get(String.valueOf(inRefFk));
371

    
372
						if (thisRef != null){
373
							if (inRef == null){
374
								logger.warn("No InRef found for nomRef: " + thisRef.getTitleCache() + "; RefId: " +  refId + "; inRefFK: " +  inRefFk);
375
							}
376
							thisRef.setInReference(inRef);
377
							refToSave.put(refId, thisRef);
378
							if(!thisRef.isProtectedTitleCache()){
379
							    thisRef.setTitleCache(null);
380
							    thisRef.getTitleCache();
381
							}
382
						}else{
383
						    logger.warn("Reference which has an inReference not found in DB. RefId: " + refId);
384
						}
385
						if(inRefFk.equals(0)){
386
						    logger.warn("InRefFk is 0 for refId "+ refId);
387
						}
388
					}
389

    
390
				} // end resultSet
391

    
392
				//save and store in map
393
				logger.info("Save in references (" + refCounter.toString() + ")");
394
				getReferenceService().saveOrUpdate(refToSave.values());
395

    
396
//			}//end resultSetList
397

    
398
//			logger.info("end makeReferences ..." + getSuccessString(success));;
399
			return success;
400
		} catch (SQLException e) {
401
			logger.error("SQLException:" +  e);
402
			return false;
403
		}
404
	}
405

    
406

    
407
	@Override
408
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, BerlinModelImportState state) {
409
		String nameSpace;
410
		Class<?> cdmClass;
411
		Set<String> idSet;
412

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

    
415
		try{
416
			Set<String> teamIdSet = new HashSet<>();
417
			Set<String> referenceIdSet = new HashSet<>();
418
			Set<String> teamStringSet = new HashSet<>();
419

    
420
			while (rs.next()){
421
				handleForeignKey(rs, teamIdSet, "NomAuthorTeamFk");
422
				handleForeignKey(rs, referenceIdSet, "InRefFk");
423
				handleForeignKey(rs, teamStringSet, "refAuthorString");
424
				//TODO only needed in second path but state not available here to check if state is second path
425
				handleForeignKey(rs, referenceIdSet, "refId");
426
			}
427

    
428
			Set<String> teamStringSet2 = new HashSet<>();
429
			for (String teamString : teamStringSet){
430
			    teamStringSet2.add(teamString.replace("'", "´"));
431
			}
432

    
433
			//team map
434
			nameSpace = BerlinModelAuthorTeamImport.NAMESPACE;
435
			cdmClass = TeamOrPersonBase.class;
436
			idSet = teamIdSet;
437
			@SuppressWarnings("unchecked")
438
            Map<String, Team> teamMap = (Map<String, Team>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
439
			result.put(nameSpace, teamMap);
440

    
441
            //refAuthor map
442
            nameSpace = REF_AUTHOR_NAMESPACE;
443
            cdmClass = TeamOrPersonBase.class;
444
            idSet = teamStringSet2;
445
            @SuppressWarnings("unchecked")
446
            Map<String, Team> refAuthorMap = (Map<String, Team>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
447
            result.put(nameSpace, refAuthorMap);
448

    
449
			//reference map
450
			nameSpace = BerlinModelReferenceImport.REFERENCE_NAMESPACE;
451
			cdmClass = Reference.class;
452
			idSet = referenceIdSet;
453
			Map<String, Reference> referenceMap = (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
454
			result.put(nameSpace, referenceMap);
455

    
456
		} catch (SQLException e) {
457
			throw new RuntimeException(e);
458
		}
459
		return result;
460
	}
461

    
462

    
463
	/**
464
	 * Handles a single reference record
465
	 * @param rs
466
	 * @param state
467
	 * @param biblioRefToSave
468
	 * @param nomRefToSave
469
	 * @param relatedBiblioReferences
470
	 * @param relatedNomReferences
471
	 * @param refCounter
472
	 * @return
473
	 */
474
	private boolean makeSingleReferenceRecord(
475
				ResultSet rs,
476
				BerlinModelImportState state,
477
				ResultSetPartitioner<BerlinModelImportState> partitioner,
478
				Map<Integer, Reference> refToSave,
479
				RefCounter refCounter){
480

    
481
	    boolean success = true;
482

    
483
		Integer refId = null;
484
		try {
485
			Map<String, Object> valueMap = getValueMap(rs);
486

    
487
			Integer categoryFk = (Integer)valueMap.get("refCategoryFk".toLowerCase());
488
			refId = (Integer)valueMap.get("refId".toLowerCase());
489
			Boolean thesisFlag = (Boolean)valueMap.get("thesisFlag".toLowerCase());
490

    
491

    
492
			Reference reference;
493
			logger.debug("RefCategoryFk: " + categoryFk);
494

    
495
			if (thesisFlag){
496
				reference = makeThesis(valueMap);
497
			}else if (categoryFk == REF_JOURNAL){
498
				reference = makeJournal(valueMap);
499
			}else if(categoryFk == REF_BOOK){
500
				reference = makeBook(valueMap);
501
			}else if(categoryFk == REF_DATABASE){
502
				reference = makeDatabase(valueMap);
503
			}else if(categoryFk == REF_INFORMAL){
504
				reference = makeInformal(valueMap);
505
			}else if(categoryFk == REF_WEBSITE){
506
				reference = makeWebSite(valueMap);
507
			}else if(categoryFk == REF_UNKNOWN){
508
				reference = makeUnknown(valueMap);
509
			}else if(categoryFk == REF_PRINT_SERIES){
510
				reference = makePrintSeries(valueMap);
511
			}else if(categoryFk == REF_CONFERENCE_PROCEEDINGS){
512
				reference = makeProceedings(valueMap);
513
			}else if(categoryFk == REF_ARTICLE){
514
				reference = makeArticle(valueMap);
515
			}else if(categoryFk == REF_JOURNAL_VOLUME){
516
				reference = makeJournalVolume(valueMap);
517
			}else if(categoryFk == REF_PART_OF_OTHER_TITLE){
518
				reference = makePartOfOtherTitle(valueMap);
519
			}else{
520
				logger.warn("Unknown categoryFk (" + categoryFk + "). Create 'Generic instead'");
521
				reference = ReferenceFactory.newGeneric();
522
				success = false;
523
			}
524

    
525
			//refYear
526
			String refYear = (String)valueMap.get("refYear".toLowerCase());
527
			reference.setDatePublished(ImportHelper.getDatePublished(refYear));
528

    
529
			handleEdition(reference);
530

    
531
			//created, updated, notes
532
			doCreatedUpdatedNotes(state, reference, rs);
533

    
534
			//idInSource (import from older source to berlin model)
535
			//TODO do we want this being imported? Maybe as alternatvie identifier?
536
			String idInSource = (String)valueMap.get("IdInSource".toLowerCase());
537
			if (isNotBlank(idInSource)){
538
				if(!state.getConfig().isDoSourceNumber()){
539
				    IdentifiableSource source = IdentifiableSource.NewDataImportInstance(idInSource);
540
				    source.setIdNamespace("import to Berlin Model");
541
				    reference.addSource(source);
542
				}else{
543
				    makeSourceNumbers(state, idInSource, reference, refId);
544
				}
545
			}
546

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

    
550

    
551
		} catch (Exception e) {
552
			logger.warn("Reference with BM refId '" + CdmUtils.Nz(refId) +  "' threw Exception and could not be saved");
553
			e.printStackTrace();
554
			success = false;
555
		}
556
		return success;
557
	}
558

    
559

    
560
	/**
561
     * @param state
562
     * @param idInSource
563
     * @param reference
564
     * @param refId
565
     */
566
    private void makeSourceNumbers(BerlinModelImportState state, String idInSource, Reference reference,
567
            Integer refId) {
568
        String[] splits = idInSource.split("\\|");
569
        for (String split : splits){
570
            split = split.trim();
571
            UUID uuid = BerlinModelTransformer.uuidEMReferenceSourceNumber;
572
            TermVocabulary<DefinedTerm> voc = null;  //user defined voc
573
            DefinedTerm type = getIdentiferType(state, uuid, "E+M Reference Source Number", "Euro+Med Reference Source Number", "E+M Source Number", voc);
574
            Identifier.NewInstance(reference, split, type);
575
        }
576
    }
577

    
578
    /**
579
     * @param reference
580
     */
581
    private void handleEdition(Reference reference) {
582
        if (reference.getEdition()!= null && reference.getEdition().startsWith("ed. ")){
583
            reference.setEdition(reference.getEdition().substring(4));
584
        }
585

    
586
    }
587

    
588
    /**
589
	 * Creates and saves a nom. reference and a biblio. reference after checking necessity
590
	 * @param rs
591
	 * @param refId
592
	 * @param ref
593
	 * @param refCounter
594
	 * @param biblioRefToSave
595
	 * @param nomRefToSave
596
	 * @param teamMap
597
	 * @param stores
598
	 * @return
599
	 * @throws SQLException
600
	 */
601
	private boolean makeNomAndBiblioReference(
602
				ResultSet rs,
603
				BerlinModelImportState state,
604
				@SuppressWarnings("rawtypes") ResultSetPartitioner partitioner,
605
				int refId,
606
				Reference ref,
607
				RefCounter refCounter,
608
				Map<Integer, Reference> refToSave
609
			) throws SQLException{
610

    
611
		@SuppressWarnings("unchecked")
612
        Map<String, Team> teamMap = partitioner.getObjectMap(BerlinModelAuthorTeamImport.NAMESPACE);
613

    
614
		String refCache = trim(rs.getString("refCache"));
615
		String nomRefCache = trim(rs.getString("nomRefCache"));
616
		String title = trim(rs.getString("title"));
617
		String nomTitleAbbrev = trim(rs.getString("nomTitleAbbrev"));
618
		boolean isPreliminary = rs.getBoolean("PreliminaryFlag");
619
		String refAuthorString = trim(rs.getString("refAuthorString"));
620
		Integer nomAuthorTeamFk = nullSafeInt(rs, "NomAuthorTeamFk");
621
		Integer inRefFk = nullSafeInt(rs, "inRefFk");
622

    
623

    
624
		TeamOrPersonBase<?> nomAuthor = null;
625
		if (nomAuthorTeamFk != null){
626
		    String strNomAuthorTeamFk = String.valueOf(nomAuthorTeamFk);
627
		    nomAuthor = teamMap.get(strNomAuthorTeamFk);
628
		    if (nomAuthor == null){
629
		        logger.warn("NomAuthor ("+strNomAuthorTeamFk+") not found in teamMap (but it should exist) for " + refId);
630
		    }
631
		}
632

    
633
		Reference sourceReference = state.getTransactionalSourceReference();
634

    
635
		//preliminary
636
		if (isPreliminary){
637
			ref.setAbbrevTitleCache(nomRefCache, true);
638
			ref.setTitleCache(refCache, true);
639
		}
640

    
641
		//title/abbrevTitle
642
		if (isNotBlank(nomTitleAbbrev)){
643
			ref.setAbbrevTitle(nomTitleAbbrev);
644
		}
645
		if (isNotBlank(title)){
646
			ref.setTitle(title);
647
		}
648

    
649
		//author
650
		TeamOrPersonBase<?> author = getAuthorship(state, refAuthorString, nomAuthor, refId);
651
		ref.setAuthorship(author);
652

    
653
		if (ref.getType().equals(ReferenceType.Book)){
654
		    extraktBookVolume(ref);
655
		}
656

    
657
		//inRef
658
		Reference inRef = null;
659
		if (inRefFk != null){
660
		    @SuppressWarnings({"unchecked" })
661
		    Map<String, Reference>  relatedReferences = partitioner.getObjectMap(REFERENCE_NAMESPACE);
662
		    inRef = relatedReferences.get(String.valueOf(inRefFk));
663
		    if (inRef == null){
664
		        inRef = refToSave.get(inRefFk);
665
		    }
666
		    if (inRef == null){
667
		        logger.warn("InRef not (yet) found. RefId: " + refId + "; InRef: "+ inRefFk);
668
		    }else{
669
		        ref.setInReference(inRef);
670
		    }
671
		}
672

    
673
		Reference result = deduplicateReference(state, ref);
674
		if(ref != result){
675
		    //dedup not possible at this point because inRef exists but is not yet defined
676
		    if (inRefFk != null && inRef == null){
677
		        result = ref;
678
		        logger.warn("Ref has deduplication candidate but inRef is still missing. " + inRef);
679
		    }else{
680
		        logger.debug("Reference was deduplicated. RefId: " + refId);
681
		        //FIXME also check annotations etc. for deduplication
682
		        refCounter.dedupCount++;
683
		    }
684
		}else{
685
		    refCounter.refCount++;
686
		}
687

    
688
		//save
689
		if (! refToSave.containsKey(refId)){
690
			refToSave.put(refId, result);
691
		}else{
692
		    //should not happen
693
			logger.warn("Duplicate refId in Berlin Model database. Second reference was not imported !!");
694
		}
695

    
696

    
697
		//refId
698
		ImportHelper.setOriginalSource(result, sourceReference, refId, REFERENCE_NAMESPACE);
699

    
700
		if (commonNameRefSet != null && commonNameRefSet.contains(refId)){
701
		    result.addMarker(Marker.NewInstance(MarkerType.COMMON_NAME_REFERENCE(), true));
702
        }
703

    
704
		return true;
705
	}
706

    
707
	/**
708
     * @param string
709
     * @return
710
     */
711
    private String trim(String string) {
712
        if (string == null){
713
            return null;
714
        }else{
715
            return string.trim();
716
        }
717
    }
718

    
719
    /**
720
	 * Copies the created and updated information from the nomReference to the cloned bibliographic reference
721
	 * @param referenceBase
722
	 * @param nomReference
723
	 */
724
	private void copyCreatedUpdated(Reference biblioReference, Reference nomReference) {
725
		biblioReference.setCreatedBy(nomReference.getCreatedBy());
726
		biblioReference.setCreated(nomReference.getCreated());
727
		biblioReference.setUpdatedBy(nomReference.getUpdatedBy());
728
		biblioReference.setUpdated(nomReference.getUpdated());
729

    
730
	}
731

    
732
	private Reference makeArticle (Map<String, Object> valueMap){
733

    
734
		IArticle article = ReferenceFactory.newArticle();
735
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
736
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
737
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
738

    
739
		if (inRefFk != null){
740
			if (inRefCategoryFk != REF_JOURNAL){
741
				logger.warn("Wrong inrefCategory for Article (refID = " + refId +"). Type must be 'Journal' but was not (RefCategoryFk=" + inRefCategoryFk + "))." +
742
					" InReference was added anyway! ");
743
			}
744
		}else{
745
			logger.warn ("Article has no inreference: " + refId);
746
		}
747
		makeStandardMapper(valueMap, (Reference)article); //url, pages, series, volume
748
		String url = (String)valueMap.get("url");
749
		if (url != null && url.contains("dx.doi.org")){
750
		    article.setDoi(DOI.fromString(url));
751
		    article.setUri(null);
752
		}
753
		return (Reference)article;
754
	}
755

    
756
	private Reference makePartOfOtherTitle (Map<String, Object> valueMap){
757

    
758
		Reference result;
759
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
760
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
761
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
762

    
763
		if (inRefCategoryFk == null){
764
			//null -> error
765
			logger.warn("Part-Of-Other-Title has no inRefCategoryFk! RefId = " + refId + ". ReferenceType set to Generic.");
766
			result = makeUnknown(valueMap);
767
		}else if (inRefFk == null){
768
			//TODO is this correct ??
769
			logger.warn("Part-Of-Other-Title has no in reference: " + refId);
770
			result = makeUnknown(valueMap);
771
		}else if (inRefCategoryFk == REF_BOOK){
772
			//BookSection
773
			IBookSection bookSection = ReferenceFactory.newBookSection();
774
			result = (Reference)bookSection;
775
		}else if (inRefCategoryFk == REF_ARTICLE){
776
			//Article
777
			logger.info("Reference (refId = " + refId + ") of type 'part_of_other_title' is part of 'article'." +
778
					" We use the section reference type for such in references now.") ;
779
			result = ReferenceFactory.newSection();
780
		}else if (inRefCategoryFk == REF_JOURNAL){
781
			//TODO
782
			logger.warn("Reference (refId = " + refId + ") of type 'part_of_other_title' has inReference of type 'journal'." +
783
					" This is not allowed! Generic reference created instead") ;
784
			result = ReferenceFactory.newGeneric();
785
			result.addMarker(Marker.NewInstance(MarkerType.TO_BE_CHECKED(), true));
786
		}else if (inRefCategoryFk == REF_PART_OF_OTHER_TITLE){
787
			logger.info("Reference (refId = " + refId + ") of type 'part_of_other_title' has inReference 'part of other title'." +
788
					" 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") ;
789
			result = ReferenceFactory.newSection();
790
		}else{
791
			logger.warn("InReference type (catFk = " + inRefCategoryFk + ") of part-of-reference not recognized for refId " + refId + "." +
792
				" Create 'Generic' reference instead");
793
			result = ReferenceFactory.newGeneric();
794
		}
795
		makeStandardMapper(valueMap, result); //url, pages
796
		return result;
797
	}
798

    
799

    
800
	/**
801
	 * @param inRefFkInt
802
	 * @param biblioRefToSave
803
	 * @param nomRefToSave
804
	 * @param relatedBiblioReferences
805
	 * @param relatedNomReferences
806
	 * @return
807
	 */
808
	private boolean existsInMapOrToSave(Integer inRefFkInt, Map<Integer, Reference> refToSave, Map<String, Reference> relatedReferences) {
809
		boolean result = false;
810
		if (inRefFkInt == null){
811
			return false;
812
		}
813
		result |= refToSave.containsKey(inRefFkInt);
814
		result |= relatedReferences.containsKey(String.valueOf(inRefFkInt));
815
		return result;
816
	}
817

    
818
	private Reference makeWebSite(Map<String, Object> valueMap){
819
		if (logger.isDebugEnabled()){logger.debug("RefType 'Website'");}
820
		Reference webPage = ReferenceFactory.newWebPage();
821
		makeStandardMapper(valueMap, webPage); //placePublished, publisher
822
		return webPage;
823
	}
824

    
825
	private Reference makeUnknown(Map<String, Object> valueMap){
826
		if (logger.isDebugEnabled()){logger.debug("RefType 'Unknown'");}
827
		Reference generic = ReferenceFactory.newGeneric();
828
//		generic.setSeries(series);
829
		makeStandardMapper(valueMap, generic); //pages, placePublished, publisher, series, volume
830
		return generic;
831
	}
832

    
833
	private Reference makeInformal(Map<String, Object> valueMap){
834
		if (logger.isDebugEnabled()){logger.debug("RefType 'Informal'");}
835
		Reference generic = ReferenceFactory.newGeneric();
836
//		informal.setSeries(series);
837
		makeStandardMapper(valueMap, generic);//editor, pages, placePublished, publisher, series, volume
838
		String informal = (String)valueMap.get("InformalRefCategory".toLowerCase());
839
		if (isNotBlank(informal) ){
840
			generic.addExtension(informal, ExtensionType.INFORMAL_CATEGORY());
841
		}
842
		return generic;
843
	}
844

    
845
	private Reference makeDatabase(Map<String, Object> valueMap){
846
		if (logger.isDebugEnabled()){logger.debug("RefType 'Database'");}
847
		Reference database =  ReferenceFactory.newDatabase();
848
		makeStandardMapper(valueMap, database); //?
849
		return database;
850
	}
851

    
852
	private Reference makeJournal(Map<String, Object> valueMap){
853
		if (logger.isDebugEnabled()){logger.debug("RefType 'Journal'");}
854
		Reference journal = ReferenceFactory.newJournal();
855

    
856
		Set<String> omitAttributes = new HashSet<>();
857
		String series = "series";
858
//		omitAttributes.add(series);
859

    
860
		makeStandardMapper(valueMap, journal, omitAttributes); //issn,placePublished,publisher
861
//		if (valueMap.get(series) != null){
862
//			logger.warn("Series not yet implemented for journal!");
863
//		}
864
		return journal;
865
	}
866

    
867
	private Reference makeBook(
868
				Map<String, Object> valueMap){
869

    
870
		if (logger.isDebugEnabled()){logger.debug("RefType 'Book'");}
871
		Reference book = ReferenceFactory.newBook();
872
//		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
873

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

    
876
		Set<String> omitAttributes = new HashSet<>();
877
		String attrSeries = "series";
878
//		omitAttributes.add(attrSeries);
879

    
880
		makeStandardMapper(valueMap, book, omitAttributes);
881

    
882
		//Series (as String)
883
		IPrintSeries printSeries = null;
884
		if (valueMap.get(attrSeries) != null){
885
			String series = (String)valueMap.get("title".toLowerCase());
886
			if (series == null){
887
				String nomTitle = (String)valueMap.get("nomTitleAbbrev".toLowerCase());
888
				series = nomTitle;
889
			}
890
			printSeries = ReferenceFactory.newPrintSeries(series);
891
			logger.info("Implementation of printSeries is preliminary");
892
		}
893
		//Series (as Reference)
894
		if (book.getInSeries() != null && printSeries != null){
895
			logger.warn("Book has series string and inSeries reference. Can not take both. Series string neglected");
896
		}else{
897
			book.setInSeries(printSeries);
898
		}
899
		book.setEditor(null);
900

    
901
		return book;
902

    
903
	}
904

    
905

    
906
	int parsedBookVolumes = 0;
907
    private void extraktBookVolume(Reference book) {
908
        if (isExtractBookVolumeCandidate(book)){
909
            String patternStr = "(.{2,})\\s(\\d{1,2})";
910
            int groupIndex = 2;
911
            Pattern pattern = Pattern.compile(patternStr);
912

    
913
            String abbrevCache = book.getAbbrevTitleCache();
914
            String titleCache = book.getTitleCache();
915
            String vol = null;
916
            String volFull = null;
917
            String abbrev = book.getAbbrevTitle();
918
            if (isNotBlank(abbrev)){
919
                Matcher matcher = pattern.matcher(abbrev);
920
                if (matcher.matches()){
921
                    vol = matcher.group(groupIndex);
922
                    abbrev = matcher.group(1);
923
                }
924
            }
925

    
926
            String full = book.getTitle();
927
            if (isNotBlank(full)){
928
                Matcher matcher = pattern.matcher(full);
929
                if (matcher.matches()){
930
                    volFull = matcher.group(groupIndex);
931
                    full = matcher.group(1);
932
                }
933
            }
934
            if (vol != null && volFull != null){
935
                if (!vol.equals(volFull)){
936
                    return;
937
                }
938
            }else if (vol == null && volFull == null){
939
                return;
940
            }else if (vol == null){
941
                if (isNotBlank(abbrev)){
942
                    return;
943
                }else{
944
                    vol = volFull;
945
                }
946
            }else if (volFull == null){
947
                if (isNotBlank(full)){
948
                    return;
949
                }
950
            }else{
951
                logger.warn("Should not happen");
952
            }
953
            book.setVolume(vol);
954
            book.setAbbrevTitle(abbrev);
955
            book.setTitle(full);
956
            if (!book.getAbbrevTitleCache().equals(abbrevCache)){
957
                logger.warn("Abbrev title cache for parsed book volume does not match: " + book.getAbbrevTitleCache() + " <-> "+abbrevCache);
958
            }else if (!book.getTitleCache().equals(titleCache)){
959
                logger.warn("Title cache for parsed book volume does not match: " + book.getTitleCache() + " <-> "+titleCache);
960
            }else{
961
//                System.out.println(titleCache);
962
//                System.out.println(abbrevCache);
963
                parsedBookVolumes++;
964
            }
965
        }else{
966
            return;
967
        }
968
    }
969

    
970
    /**
971
     * @param book
972
     * @return
973
     */
974
    private boolean isExtractBookVolumeCandidate(Reference book) {
975
        if (isNotBlank(book.getVolume()) || isNotBlank(book.getEdition()) || isNotBlank(book.getSeriesPart())){
976
            return false;
977
        }
978
        if (!checkExtractBookVolumeTitle(book.getAbbrevTitle())){
979
            return false;
980
        }
981
        if (!checkExtractBookVolumeTitle(book.getTitle())){
982
            return false;
983
        }
984
        return true;
985
    }
986

    
987
    /**
988
     * @param abbrevTitle
989
     * @return
990
     */
991
    private boolean checkExtractBookVolumeTitle(String title) {
992
        if (title == null){
993
            return true;
994
        }
995
        if (title.contains(",") || title.contains("ed.") || title.contains("Ed.")|| title.contains("Suppl")
996
                || title.contains("Ser.")|| title.contains("ser.")) {
997
            return false;
998
        }
999
        return true;
1000
    }
1001

    
1002
    /**
1003
	 * Returns the requested object if it exists in one of both maps. Prefers the refToSaveMap in ambigious cases.
1004
	 * @param inRefFkInt
1005
	 * @param nomRefToSave
1006
	 * @param relatedNomReferences
1007
	 * @return
1008
	 */
1009
	private Reference getReferenceFromMaps(
1010
			int inRefFkInt,
1011
			Map<Integer, Reference> refToSaveMap,
1012
			Map<String, Reference> relatedRefMap) {
1013
		Reference result = null;
1014
		result = refToSaveMap.get(inRefFkInt);
1015
		if (result == null){
1016
			result = relatedRefMap.get(String.valueOf(inRefFkInt));
1017
		}
1018
		return result;
1019
	}
1020

    
1021
	private Reference makePrintSeries(Map<String, Object> valueMap){
1022
		if (logger.isDebugEnabled()){logger.debug("RefType 'PrintSeries'");}
1023
		Reference printSeries = ReferenceFactory.newPrintSeries();
1024
		makeStandardMapper(valueMap, printSeries, null);
1025
		return printSeries;
1026
	}
1027

    
1028
	private Reference makeProceedings(Map<String, Object> valueMap){
1029
		if (logger.isDebugEnabled()){logger.debug("RefType 'Proceedings'");}
1030
		Reference proceedings = ReferenceFactory.newProceedings();
1031
		makeStandardMapper(valueMap, proceedings, null);
1032
		return proceedings;
1033
	}
1034

    
1035
	private Reference makeThesis(Map<String, Object> valueMap){
1036
		if (logger.isDebugEnabled()){logger.debug("RefType 'Thesis'");}
1037
		Reference thesis = ReferenceFactory.newThesis();
1038
		makeStandardMapper(valueMap, thesis, null);
1039
		return thesis;
1040
	}
1041

    
1042

    
1043
	private Reference makeJournalVolume(Map<String, Object> valueMap){
1044
		if (logger.isDebugEnabled()){logger.debug("RefType 'JournalVolume'");}
1045
		//Proceedings proceedings = Proceedings.NewInstance();
1046
		Reference journalVolume = ReferenceFactory.newGeneric();
1047
		makeStandardMapper(valueMap, journalVolume, null);
1048
		logger.warn("Journal volumes not yet implemented. Generic created instead but with errors");
1049
		return journalVolume;
1050
	}
1051

    
1052
	private boolean makeStandardMapper(Map<String, Object> valueMap, Reference ref){
1053
		return makeStandardMapper(valueMap, ref, null);
1054
	}
1055

    
1056

    
1057
	private boolean makeStandardMapper(Map<String, Object> valueMap, CdmBase cdmBase, Set<String> omitAttributes){
1058
		boolean result = true;
1059
		for (CdmAttributeMapperBase mapper : classMappers){
1060
			if (mapper instanceof CdmSingleAttributeMapperBase){
1061
				result &= makeStandardSingleMapper(valueMap, cdmBase, (CdmSingleAttributeMapperBase)mapper, omitAttributes);
1062
			}else if (mapper instanceof CdmOneToManyMapper){
1063
				result &= makeMultipleValueAddMapper(valueMap, cdmBase, (CdmOneToManyMapper)mapper, omitAttributes);
1064
			}else{
1065
				logger.error("Unknown mapper type");
1066
				result = false;
1067
			}
1068
		}
1069
		return result;
1070
	}
1071

    
1072
	private boolean makeStandardSingleMapper(Map<String, Object> valueMap, CdmBase cdmBase, CdmSingleAttributeMapperBase mapper, Set<String> omitAttributes){
1073
		boolean result = true;
1074
		if (omitAttributes == null){
1075
			omitAttributes = new HashSet<>();
1076
		}
1077
		if (mapper instanceof DbImportExtensionMapper){
1078
			result &= ((DbImportExtensionMapper)mapper).invoke(valueMap, cdmBase);
1079
		}else if (mapper instanceof DbImportMarkerMapper){
1080
			result &= ((DbImportMarkerMapper)mapper).invoke(valueMap, cdmBase);
1081
		}else{
1082
			String sourceAttribute = mapper.getSourceAttributeList().get(0).toLowerCase();
1083
			Object value = valueMap.get(sourceAttribute);
1084
			if (mapper instanceof CdmUriMapper && value != null){
1085
				try {
1086
					value = new URI (value.toString());
1087
				} catch (URISyntaxException e) {
1088
					logger.error("URI syntax exception: " + value.toString());
1089
					value = null;
1090
				}
1091
			}
1092
			if (value != null){
1093
				String destinationAttribute = mapper.getDestinationAttribute();
1094
				if (! omitAttributes.contains(destinationAttribute)){
1095
					result &= ImportHelper.addValue(value, cdmBase, destinationAttribute, mapper.getTypeClass(), OVERWRITE, OBLIGATORY);
1096
				}
1097
			}
1098
		}
1099
		return result;
1100
	}
1101

    
1102

    
1103
	private boolean makeMultipleValueAddMapper(Map<String, Object> valueMap, CdmBase cdmBase, CdmOneToManyMapper<CdmBase, CdmBase, CdmSingleAttributeMapperBase> mapper, Set<String> omitAttributes){
1104
		if (omitAttributes == null){
1105
			omitAttributes = new HashSet<>();
1106
		}
1107
		boolean result = true;
1108
		String destinationAttribute = mapper.getSingleAttributeName();
1109
		List<Object> sourceValues = new ArrayList<>();
1110
		List<Class> classes = new ArrayList<>();
1111
		for (CdmSingleAttributeMapperBase singleMapper : mapper.getSingleMappers()){
1112
			String sourceAttribute = singleMapper.getSourceAttribute();
1113
			Object value = valueMap.get(sourceAttribute);
1114
			sourceValues.add(value);
1115
			Class<?> clazz = singleMapper.getTypeClass();
1116
			classes.add(clazz);
1117
		}
1118

    
1119
		result &= ImportHelper.addMultipleValues(sourceValues, cdmBase, destinationAttribute, classes, NO_OVERWRITE, OBLIGATORY);
1120
		return result;
1121
	}
1122

    
1123

    
1124
	private TeamOrPersonBase<?> getAuthorship(BerlinModelImportState state, String refAuthorString,
1125
	        TeamOrPersonBase<?> nomAuthor, Integer refId){
1126

    
1127
	    TeamOrPersonBase<?> result;
1128
		if (nomAuthor != null){
1129
			result = nomAuthor;
1130
			if (isNotBlank(refAuthorString) && !nomAuthor.getTitleCache().equals(refAuthorString)){
1131
			    boolean isSimilar = handleSimilarAuthors(state, refAuthorString, nomAuthor, refId);
1132
			    if (! isSimilar){
1133
			        String message = "refAuthorString differs from nomAuthor.titleCache: " + refAuthorString
1134
                            + " <-> " + nomAuthor.getTitleCache() + "; RefId: " + refId;
1135
			        logger.warn(message);
1136
			    }
1137
			}
1138
		} else if (isNotBlank(refAuthorString)){//only RefAuthorString exists
1139
		    refAuthorString = refAuthorString.trim();
1140
			//TODO match with existing Persons/Teams
1141
		    TeamOrPersonBase<?> author = state.getRelatedObject(REF_AUTHOR_NAMESPACE, refAuthorString, TeamOrPersonBase.class);
1142
			if (author == null){
1143
			    if (!BerlinModelAuthorTeamImport.hasTeamSeparator(refAuthorString)){
1144
			        author = makePerson(refAuthorString, false, refId);
1145
			    }else{
1146
			        author = makeTeam(state, refAuthorString, refId);
1147
			    }
1148
			    state.addRelatedObject(REF_AUTHOR_NAMESPACE, refAuthorString, author);
1149
			    result = deduplicatePersonOrTeam(state, author);
1150

    
1151
			    if (result != author){
1152
                    logger.debug("RefAuthorString author deduplicated " + author);
1153
                }else{
1154
                    if (!importSourceExists(author, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference() )){
1155
                        author.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1156
                    }
1157
                }
1158
			}else{
1159
			    logger.debug("RefAuthor loaded from map");
1160
			}
1161
			result = author;
1162
		}else{
1163
			result = null;
1164
		}
1165

    
1166
		return result;
1167
	}
1168

    
1169

    
1170
    /**
1171
     * @param state
1172
     * @param refAuthorString
1173
     * @param refId
1174
     * @return
1175
     */
1176
    private TeamOrPersonBase<?> makeTeam(BerlinModelImportState state, String refAuthorString, Integer refId) {
1177
        Team team = Team.NewInstance();
1178
        boolean hasDedupMember = false;
1179
        if (containsEdOrColon(refAuthorString)){
1180
            team.setTitleCache(refAuthorString, true);
1181
        }else{
1182
            String[] refAuthorTeams = BerlinModelAuthorTeamImport.splitTeam(refAuthorString);
1183
            boolean lastWasInitials = false;
1184
            for (int i = 0; i< refAuthorTeams.length ;i++){
1185
                if (lastWasInitials){
1186
                    lastWasInitials = false;
1187
                    continue;
1188
                }
1189
                String fullTeam = refAuthorTeams[i].trim();
1190
                String initials = null;
1191
                if (refAuthorTeams.length > i+1){
1192
                    String nextSplit = refAuthorTeams[i+1].trim();
1193
                    if (isInitial(nextSplit)){
1194
                        lastWasInitials = true;
1195
                        initials = nextSplit;
1196
                    }
1197
                }
1198
                Person member = makePerson(fullTeam, isNotBlank(initials), refId);
1199

    
1200
                if (initials != null){
1201
                    if (member.getInitials() != null){
1202
                        logger.warn("Initials already set: " + refId);
1203
                    }else if (!member.isProtectedTitleCache()){
1204
                        member.setInitials(initials);
1205
                    }else {
1206
                        member.setTitleCache(member.getTitleCache() + ", " + initials, true);
1207
                    }
1208
                }
1209

    
1210
                if (i == refAuthorTeams.length -1 && BerlinModelAuthorTeamImport.isEtAl(member)){
1211
                    team.setHasMoreMembers(true);
1212
                }else{
1213
                    Person dedupMember = deduplicatePersonOrTeam(state, member);
1214
                    if (dedupMember != member){
1215
                        hasDedupMember = true;
1216
                    }else{
1217
                        if (!importSourceExists(member, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference())){
1218
                            member.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1219
                        }
1220
                    }
1221

    
1222
                    team.addTeamMember(dedupMember);
1223
                }
1224
            }
1225
        }
1226

    
1227
        TeamOrPersonBase<?> result = team;
1228
        if (team.getTeamMembers().size() == 1 && !team.isHasMoreMembers()){
1229
            Person person = team.getTeamMembers().get(0);
1230
            checkPerson(person, refAuthorString, hasDedupMember, refId);
1231
            result = person;
1232
        }else{
1233
            checkTeam(team, refAuthorString, refId);
1234
            result = team;
1235
        }
1236

    
1237
        return result;
1238
    }
1239

    
1240
    /**
1241
     * @param team
1242
     * @param refAuthorString
1243
     * @param refId
1244
     */
1245
    private static void checkTeam(Team team, String refAuthorString, Integer refId) {
1246
        TeamDefaultCacheStrategy formatter = (TeamDefaultCacheStrategy) team.getCacheStrategy();
1247
        formatter.setEtAlPosition(100);
1248
        if (formatter.getTitleCache(team).equals(refAuthorString)){
1249
            team.setProtectedTitleCache(false);
1250
        }else if(formatter.getTitleCache(team).replace(" & ", ", ").equals(refAuthorString.replace(" & ", ", ").replace(" ,", ","))){
1251
            //also accept teams with ', ' as final member separator as not protected
1252
            team.setProtectedTitleCache(false);
1253
        }else if(formatter.getFullTitle(team).replace(" & ", ", ").equals(refAuthorString.replace(" & ", ", "))){
1254
            //.. or teams with initials first
1255
            team.setProtectedTitleCache(false);
1256
        }else if (containsEdOrColon(refAuthorString)){
1257
            //nothing to do, it is expected to be protected
1258

    
1259
        }else{
1260
            team.setTitleCache(refAuthorString, true);
1261
            logger.warn("Creation of titleCache for team with members did not (fully) work: " + refAuthorString + " <-> " + formatter.getTitleCache(team)+ " : " + refId);
1262
        }
1263

    
1264
    }
1265

    
1266
    /**
1267
     * @param hasDedupMember
1268
     * @param result
1269
     * @return
1270
     */
1271
    private static void checkPerson(Person person, String refAuthorString, boolean hasDedupMember, Integer refId) {
1272
        PersonDefaultCacheStrategy formatter = (PersonDefaultCacheStrategy) person.getCacheStrategy();
1273

    
1274
        String oldTitleCache = person.getTitleCache();
1275
        boolean oldTitleCacheProtected = person.isProtectedTitleCache();
1276

    
1277
        if (! oldTitleCache.equals(refAuthorString)){
1278
            logger.error("Old titleCache does not equal refAuthorString this should not happen. "+ oldTitleCache + " <-> " + refAuthorString + "; refId = " + refId);
1279
        }
1280

    
1281
        boolean protect = true;
1282
        person.setProtectedTitleCache(false);
1283
        if (refAuthorString.equals(formatter.getTitleCache(person))){
1284
            protect = false;
1285
        }else if(formatter.getFullTitle(person).equals(refAuthorString)){
1286
            //.. or teams with initials first
1287
            protect = false;
1288
        }else{
1289
            //keep protected, see below
1290
        }
1291

    
1292
        if (hasDedupMember){
1293
            //restore
1294
            //TODO maybe even do not use dedup for testing
1295
            person.setTitleCache(oldTitleCache, oldTitleCacheProtected);
1296
            if (protect != oldTitleCacheProtected){
1297
                logger.warn("Deduplicated person protection requirement unclear for "+refAuthorString+". New:"+protect+"/Old:"+oldTitleCacheProtected+"; RefId: " + refId);
1298
            }
1299
        }else{
1300
            if (protect){
1301
                logger.warn("Creation of titleCache for person (converted from team) with members did not (fully) work: " + refAuthorString + " <-> " + formatter.getTitleCache(person)+ " : " + refId);
1302
                person.setTitleCache(refAuthorString, protect);
1303
            }else{
1304
                //keep unprotected
1305
            }
1306
        }
1307
    }
1308

    
1309
    /**
1310
     * @param refAuthorString
1311
     * @return
1312
     */
1313
    private static boolean containsEdOrColon(String str) {
1314
        if (str.contains(" ed.") || str.contains(" Ed.") || str.contains("(ed.")
1315
                || str.contains("[ed.") || str.contains("(Eds)") || str.contains("(Eds.)") ||
1316
                str.contains("(eds.)") || str.contains(":")|| str.contains(";") || str.contains("Publ. & Inform. Directorate")
1317
                || str.contains("Anonymous [Department of Botany, Faculty of Science, FER-ZPR, University of Zagreb]")
1318
                || str.contains("Davis, P. H. (Güner, A. & al.)")){
1319
            return true;
1320
        }else{
1321
            return false;
1322
        }
1323
    }
1324

    
1325
    /**
1326
     * @param nextSplit
1327
     * @return
1328
     */
1329
    private static boolean isInitial(String str) {
1330
        if (str == null){
1331
            return false;
1332
        }
1333
        boolean matches = str.trim().matches("(\\p{javaUpperCase}|Yu|Ya|Th|Ch|Lj|Sz|Dz|Sh|Ju|R. M. da S)\\.?"
1334
                + "(\\s*[-\\s]\\s*(\\p{javaUpperCase}|Yu|Ja|Kh|Tz|Ya|Th|Ju)\\.?)*(\\s+(van|von|de|de la|del|da|van der))?");
1335
        return matches;
1336
    }
1337

    
1338
    private <T extends TeamOrPersonBase<?>> T deduplicatePersonOrTeam(BerlinModelImportState state,T author) {
1339
        T result = deduplicationHelper.getExistingAuthor(state, author);
1340
        return result;
1341
    }
1342

    
1343
    private Reference deduplicateReference(BerlinModelImportState state,Reference ref) {
1344
        Reference result = deduplicationHelper.getExistingReference(state, ref);
1345
        return result;
1346
    }
1347

    
1348
    private static Person makePerson(String full, boolean followedByInitial, Integer refId) {
1349
        Person person = Person.NewInstance();
1350
        person.setTitleCache(full, true);
1351
        if (!full.matches(".*[\\s\\.].*")){
1352
            person.setFamilyName(full);
1353
            person.setProtectedTitleCache(false);
1354
        }else{
1355
            parsePerson(person, full, true, followedByInitial);
1356
        }
1357

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

    
1362
        return person;
1363
    }
1364

    
1365
    private static void parsePerson(Person person, String str, boolean preliminary, boolean followedByInitial) {
1366
        String capWord = "\\p{javaUpperCase}\\p{javaLowerCase}{2,}";
1367
        String famStart = "(Le |D'|'t |Mc|Mac|Des |d'|Du |De |Al-)";
1368
        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)("
1369
                + famStart + "?" + capWord + "((-| y | i | é | de | de la )" + capWord + ")?)";
1370
        Matcher matcher = Pattern.compile(regEx).matcher(str);
1371
        if (matcher.matches()){
1372
            person.setProtectedTitleCache(false);
1373
            String familyName = matcher.group(6).trim();
1374
            person.setFamilyName(familyName);
1375
            person.setInitials(matcher.group(1).trim());
1376
        }else{
1377
            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)";
1378
            Matcher matcher2 = Pattern.compile(regEx2).matcher(str);
1379
            if (followedByInitial && matcher2.matches()){
1380
                person.setFamilyName(str);
1381
                person.setProtectedTitleCache(false);
1382
            }else{
1383
                person.setTitleCache(str, preliminary);
1384
            }
1385
        }
1386
    }
1387

    
1388
    /**
1389
     * @param state
1390
     * @param refAuthorString
1391
     * @param nomAuthor
1392
     * @return
1393
     */
1394
    private static boolean handleSimilarAuthors(BerlinModelImportState state, String refAuthorString,
1395
            TeamOrPersonBase<?> nomAuthor, int refId) {
1396
        String nomTitle = nomAuthor.getTitleCache();
1397

    
1398
        if (refAuthorString.equals(nomAuthor.getNomenclaturalTitle())){
1399
            //nomTitle equal
1400
            return true;
1401
        }else{
1402
            if (refAuthorString.replace(" & ", ", ").equals(nomTitle.replace(" & ", ", "))){
1403
                //nomTitle equal except for "&"
1404
                return true;
1405
            }
1406
            String nomFullTitle = nomAuthor.getFullTitle();
1407
            if (refAuthorString.replace(" & ", ", ").equals(nomFullTitle.replace(" & ", ", "))){
1408
                return true;
1409
            }
1410

    
1411
            if (nomAuthor.isInstanceOf(Person.class)){
1412
                Person person = CdmBase.deproxy(nomAuthor, Person.class);
1413

    
1414
                //refAuthor has initials behind, nom Author in front // the other way round is handled in firstIsFullNameOfInitialName
1415
                if (refAuthorString.contains(",") && !nomTitle.contains(",") ){
1416
                    String[] splits = refAuthorString.split(",");
1417
                    if (splits.length == 2){
1418
                        String newMatch = splits[1].trim() + " " + splits[0].trim();
1419
                        if (newMatch.equals(nomTitle)){
1420
                            if (isBlank(person.getFamilyName())){
1421
                                person.setFamilyName(splits[0].trim());
1422
                            }
1423
                            if (isBlank(person.getInitials())){
1424
                                person.setInitials(splits[1].trim());
1425
                            }
1426
                            return true;
1427
                        }
1428
                    }
1429
                }
1430

    
1431
                if (refAuthorIsFamilyAuthorOfNomAuthor(state, refAuthorString, person)){
1432
                    return true;
1433
                }
1434

    
1435
                if (firstIsFullNameOfInitialName(state, refAuthorString, person, refId)){
1436
                    return true;
1437
                }
1438
            }
1439

    
1440
        }
1441
        return false;
1442
    }
1443

    
1444
    /**
1445
     * @param state
1446
     * @param refAuthorString
1447
     * @param person
1448
     * @return
1449
     */
1450
    private static boolean refAuthorIsFamilyAuthorOfNomAuthor(BerlinModelImportState state, String refAuthorString,
1451
            Person person) {
1452
        if (refAuthorString.equals(person.getFamilyName())){
1453
            return true;
1454
        }else{
1455
            return false;
1456
        }
1457
    }
1458

    
1459
    /**
1460
     * @param state
1461
     * @param refAuthorString
1462
     * @param nomAuthor
1463
     * @return
1464
     */
1465
    private static boolean firstIsFullNameOfInitialName(BerlinModelImportState state, String fullName,
1466
            Person initialAuthor, int refId) {
1467
        String initialName = initialAuthor.getTitleCache();
1468

    
1469
        String[] fullSplits = fullName.split(",");
1470
        String[] initialSplits = initialName.split(",");
1471

    
1472
        if (fullSplits.length == 2 && initialSplits.length == 2){
1473
            String[] fullGivenName = fullSplits[1].trim().split(" ");
1474
            String[] initialsGivenName = initialSplits[1].trim().split(" ");
1475
            boolean result = compareFamilyAndInitials(fullSplits[0], initialSplits[0], fullGivenName, initialsGivenName);
1476
            if (result){
1477
                setGivenName(state, fullSplits[1], initialAuthor, refId);
1478
            }
1479
            return result;
1480
        }else if (fullSplits.length == 1 && initialSplits.length == 2){
1481
            String[] fullSingleSplits = fullName.split(" ");
1482
            String fullFamily = fullSingleSplits[fullSingleSplits.length-1];
1483
            String[] fullGivenName = Arrays.copyOfRange(fullSingleSplits, 0, fullSingleSplits.length-1);
1484
            String[] initialsGivenName = initialSplits[1].trim().split(" ");
1485
            boolean result =  compareFamilyAndInitials(fullFamily, initialSplits[0], fullGivenName, initialsGivenName);
1486
            if (result){
1487
                if(hasAtLeastOneFullName(fullGivenName)){
1488
                    setGivenName(state, CdmUtils.concat(" ", fullGivenName), initialAuthor, refId);
1489
                }
1490
            }
1491
            return result;
1492
        }else if (fullSplits.length == 1 && initialAuthor.getInitials() == null){
1493
            //don't if this will be implemented, initialAuthors with only nomencl.Author set
1494
        }
1495

    
1496
        return false;
1497
    }
1498

    
1499
    /**
1500
     * @param fullGivenName
1501
     * @return
1502
     */
1503
    private static boolean hasAtLeastOneFullName(String[] fullGivenName) {
1504
        for (String singleName : fullGivenName){
1505
            if (!singleName.endsWith(".") && singleName.length() > 2 && !singleName.matches("(von|van)") ){
1506
                return true;
1507
            }
1508
        }
1509
        return false;
1510
    }
1511

    
1512
    /**
1513
     * @param state
1514
     * @param string
1515
     * @param initialAuthor
1516
     */
1517
    private static void setGivenName(BerlinModelImportState state, String givenName, Person person, int refId) {
1518
        givenName = givenName.trim();
1519
        if(person.getGivenName() == null || person.getGivenName().equals(givenName)){
1520
            person.setGivenName(givenName);
1521
        }else{
1522
            logger.warn("RefAuthor given name and existing given name differ: " + givenName + " <-> " + person.getGivenName() + "; RefId + " + refId);
1523
        }
1524
    }
1525

    
1526
    /**
1527
     * @param fullGivenName
1528
     * @param initialsGivenName
1529
     */
1530
    protected static boolean compareFamilyAndInitials(String fullFamilyName, String initialsFamilyName,
1531
            String[] fullGivenName, String[] initialsGivenName) {
1532
        if (!fullFamilyName.equals(initialsFamilyName)){
1533
            return false;
1534
        }
1535
        if (fullGivenName.length == initialsGivenName.length){
1536
            for (int i =0; i< fullGivenName.length ; i++){
1537
                if (fullGivenName[i].length() == 0  //comma ending not allowed
1538
                        || initialsGivenName[i].length() != 2 //only K. or similar allowed
1539
                        || fullGivenName[i].length() < initialsGivenName[i].length()  //fullFirstName must be longer than abbrev Name
1540
                        || !initialsGivenName[i].endsWith(".") //initials must end with "."
1541
                        || !fullGivenName[i].startsWith(initialsGivenName[i].replace(".", ""))){ //start with same letter
1542
                    if (fullGivenName[i].matches("(von|van|de|zu)") && fullGivenName[i].equals(initialsGivenName[i])){
1543
                        continue;
1544
                    }else{
1545
                        return false;
1546
                    }
1547
                }
1548
            }
1549
            return true;
1550
        }else{
1551
            return false;
1552
        }
1553
    }
1554

    
1555
    /**
1556
	 * @param lowerCase
1557
	 * @param config
1558
	 * @return
1559
	 */
1560
	public Set<String> getObligatoryAttributes(boolean lowerCase, BerlinModelImportConfigurator config){
1561
		Set<String> result = new HashSet<>();
1562
		Class<ICdmImport>[] ioClassList = config.getIoClassList();
1563
		logger.warn("getObligatoryAttributes has been commented because it still needs to be adapted to the new package structure");
1564
		result.addAll(Arrays.asList(unclearMappers));
1565
		result.addAll(Arrays.asList(createdAndNotesAttributes));
1566
		result.addAll(Arrays.asList(operationalAttributes));
1567
		CdmIoMapping mapping = new CdmIoMapping();
1568
		for (CdmAttributeMapperBase mapper : classMappers){
1569
			mapping.addMapper(mapper);
1570
		}
1571
		result.addAll(mapping.getSourceAttributes());
1572
		if (lowerCase){
1573
			Set<String> lowerCaseResult = new HashSet<>();
1574
			for (String str : result){
1575
				if (str != null){lowerCaseResult.add(str.toLowerCase());}
1576
			}
1577
			result = lowerCaseResult;
1578
		}
1579
		return result;
1580
	}
1581

    
1582
	@Override
1583
	protected boolean doCheck(BerlinModelImportState state){
1584
		BerlinModelReferenceImportValidator validator = new BerlinModelReferenceImportValidator();
1585
		return validator.validate(state, this);
1586
	}
1587

    
1588
	@Override
1589
	protected boolean isIgnore(BerlinModelImportState state){
1590
		return (state.getConfig().getDoReferences() == IImportConfigurator.DO_REFERENCES.NONE);
1591
	}
1592

    
1593
}
(14-14/22)