Project

General

Profile

Download (59.9 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.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.DefinedTerm;
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
    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
            String uuid = null;
547
            if (resultSetHasColumn(rs,"UUID")){
548
                uuid = rs.getString("UUID");
549
                if (uuid != null){
550
                    reference.setUuid(UUID.fromString(uuid));
551
                }
552
            }
553

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

    
557

    
558
		} catch (Exception e) {
559
			logger.warn("Reference with BM refId '" + CdmUtils.Nz(refId) +  "' threw Exception and could not be saved");
560
			e.printStackTrace();
561
			success = false;
562
		}
563
		return success;
564
	}
565

    
566

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

    
585
    /**
586
     * @param reference
587
     */
588
    private void handleEdition(Reference reference) {
589
        if (reference.getEdition()!= null && reference.getEdition().startsWith("ed. ")){
590
            reference.setEdition(reference.getEdition().substring(4));
591
        }
592

    
593
    }
594

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

    
618
		@SuppressWarnings("unchecked")
619
        Map<String, Team> teamMap = partitioner.getObjectMap(BerlinModelAuthorTeamImport.NAMESPACE);
620

    
621
		String refCache = trim(rs.getString("refCache"));
622
		String nomRefCache = trim(rs.getString("nomRefCache"));
623
		String title = trim(rs.getString("title"));
624
		String nomTitleAbbrev = trim(rs.getString("nomTitleAbbrev"));
625
		boolean isPreliminary = rs.getBoolean("PreliminaryFlag");
626
		String refAuthorString = trim(rs.getString("refAuthorString"));
627
		Integer nomAuthorTeamFk = nullSafeInt(rs, "NomAuthorTeamFk");
628
		Integer inRefFk = nullSafeInt(rs, "inRefFk");
629

    
630

    
631
		TeamOrPersonBase<?> nomAuthor = null;
632
		if (nomAuthorTeamFk != null){
633
		    String strNomAuthorTeamFk = String.valueOf(nomAuthorTeamFk);
634
		    nomAuthor = teamMap.get(strNomAuthorTeamFk);
635
		    if (nomAuthor == null){
636
		        logger.warn("NomAuthor ("+strNomAuthorTeamFk+") not found in teamMap (but it should exist) for " + refId);
637
		    }
638
		}
639

    
640
		Reference sourceReference = state.getTransactionalSourceReference();
641

    
642
		//preliminary
643
		if (isPreliminary){
644
			ref.setAbbrevTitleCache(nomRefCache, true);
645
			ref.setTitleCache(refCache, true);
646
		}
647

    
648
		//title/abbrevTitle
649
		if (isNotBlank(nomTitleAbbrev)){
650
			ref.setAbbrevTitle(nomTitleAbbrev);
651
		}
652
		if (isNotBlank(title)){
653
			ref.setTitle(title);
654
		}
655

    
656
		//author
657
		TeamOrPersonBase<?> author = getAuthorship(state, refAuthorString, nomAuthor, refId);
658
		ref.setAuthorship(author);
659

    
660
		if (ref.getType().equals(ReferenceType.Book)){
661
		    extraktBookVolume(ref);
662
		}
663

    
664
		//inRef
665
		Reference inRef = null;
666
		if (inRefFk != null){
667
		    @SuppressWarnings({"unchecked" })
668
		    Map<String, Reference>  relatedReferences = partitioner.getObjectMap(REFERENCE_NAMESPACE);
669
		    inRef = relatedReferences.get(String.valueOf(inRefFk));
670
		    if (inRef == null){
671
		        inRef = refToSave.get(inRefFk);
672
		    }
673
		    if (inRef == null){
674
		        logger.warn("InRef not (yet) found. RefId: " + refId + "; InRef: "+ inRefFk);
675
		    }else{
676
		        ref.setInReference(inRef);
677
		    }
678
		}
679

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

    
695
		//save
696
		if (! refToSave.containsKey(refId)){
697
			refToSave.put(refId, result);
698
		}else{
699
		    //should not happen
700
			logger.warn("Duplicate refId in Berlin Model database. Second reference was not imported !!");
701
		}
702

    
703

    
704
		//refId
705
		ImportHelper.setOriginalSource(result, sourceReference, refId, REFERENCE_NAMESPACE);
706

    
707
		if (commonNameRefSet != null && commonNameRefSet.contains(refId)){
708
		    result.addMarker(Marker.NewInstance(MarkerType.COMMON_NAME_REFERENCE(), true));
709
        }
710

    
711
		return true;
712
	}
713

    
714
	/**
715
     * @param string
716
     * @return
717
     */
718
    private String trim(String string) {
719
        if (string == null){
720
            return null;
721
        }else{
722
            return string.trim();
723
        }
724
    }
725

    
726
    /**
727
	 * Copies the created and updated information from the nomReference to the cloned bibliographic reference
728
	 * @param referenceBase
729
	 * @param nomReference
730
	 */
731
	private void copyCreatedUpdated(Reference biblioReference, Reference nomReference) {
732
		biblioReference.setCreatedBy(nomReference.getCreatedBy());
733
		biblioReference.setCreated(nomReference.getCreated());
734
		biblioReference.setUpdatedBy(nomReference.getUpdatedBy());
735
		biblioReference.setUpdated(nomReference.getUpdated());
736

    
737
	}
738

    
739
	private Reference makeArticle (Map<String, Object> valueMap){
740

    
741
		IArticle article = ReferenceFactory.newArticle();
742
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
743
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
744
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
745

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

    
763
	private Reference makePartOfOtherTitle (Map<String, Object> valueMap){
764

    
765
		Reference result;
766
		Object inRefFk = valueMap.get("inRefFk".toLowerCase());
767
		Integer inRefCategoryFk = (Integer)valueMap.get("inRefCategoryFk".toLowerCase());
768
		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
769

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

    
806

    
807
	/**
808
	 * @param inRefFkInt
809
	 * @param biblioRefToSave
810
	 * @param nomRefToSave
811
	 * @param relatedBiblioReferences
812
	 * @param relatedNomReferences
813
	 * @return
814
	 */
815
	private boolean existsInMapOrToSave(Integer inRefFkInt, Map<Integer, Reference> refToSave, Map<String, Reference> relatedReferences) {
816
		boolean result = false;
817
		if (inRefFkInt == null){
818
			return false;
819
		}
820
		result |= refToSave.containsKey(inRefFkInt);
821
		result |= relatedReferences.containsKey(String.valueOf(inRefFkInt));
822
		return result;
823
	}
824

    
825
	private Reference makeWebSite(Map<String, Object> valueMap){
826
		if (logger.isDebugEnabled()){logger.debug("RefType 'Website'");}
827
		Reference webPage = ReferenceFactory.newWebPage();
828
		makeStandardMapper(valueMap, webPage); //placePublished, publisher
829
		return webPage;
830
	}
831

    
832
	private Reference makeUnknown(Map<String, Object> valueMap){
833
		if (logger.isDebugEnabled()){logger.debug("RefType 'Unknown'");}
834
		Reference generic = ReferenceFactory.newGeneric();
835
//		generic.setSeries(series);
836
		makeStandardMapper(valueMap, generic); //pages, placePublished, publisher, series, volume
837
		return generic;
838
	}
839

    
840
	private Reference makeInformal(Map<String, Object> valueMap){
841
		if (logger.isDebugEnabled()){logger.debug("RefType 'Informal'");}
842
		Reference generic = ReferenceFactory.newGeneric();
843
//		informal.setSeries(series);
844
		makeStandardMapper(valueMap, generic);//editor, pages, placePublished, publisher, series, volume
845
		String informal = (String)valueMap.get("InformalRefCategory".toLowerCase());
846
		if (isNotBlank(informal) ){
847
			generic.addExtension(informal, ExtensionType.INFORMAL_CATEGORY());
848
		}
849
		return generic;
850
	}
851

    
852
	private Reference makeDatabase(Map<String, Object> valueMap){
853
		if (logger.isDebugEnabled()){logger.debug("RefType 'Database'");}
854
		Reference database =  ReferenceFactory.newDatabase();
855
		makeStandardMapper(valueMap, database); //?
856
		return database;
857
	}
858

    
859
	private Reference makeJournal(Map<String, Object> valueMap){
860
		if (logger.isDebugEnabled()){logger.debug("RefType 'Journal'");}
861
		Reference journal = ReferenceFactory.newJournal();
862

    
863
		Set<String> omitAttributes = new HashSet<>();
864
		String series = "series";
865
//		omitAttributes.add(series);
866

    
867
		makeStandardMapper(valueMap, journal, omitAttributes); //issn,placePublished,publisher
868
//		if (valueMap.get(series) != null){
869
//			logger.warn("Series not yet implemented for journal!");
870
//		}
871
		return journal;
872
	}
873

    
874
	private Reference makeBook(
875
				Map<String, Object> valueMap){
876

    
877
		if (logger.isDebugEnabled()){logger.debug("RefType 'Book'");}
878
		Reference book = ReferenceFactory.newBook();
879
//		Integer refId = (Integer)valueMap.get("refId".toLowerCase());
880

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

    
883
		Set<String> omitAttributes = new HashSet<>();
884
		String attrSeries = "series";
885
//		omitAttributes.add(attrSeries);
886

    
887
		makeStandardMapper(valueMap, book, omitAttributes);
888

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

    
908
		return book;
909

    
910
	}
911

    
912

    
913
	int parsedBookVolumes = 0;
914
    private void extraktBookVolume(Reference book) {
915
        if (isExtractBookVolumeCandidate(book)){
916
            String patternStr = "(.{2,})\\s(\\d{1,2})";
917
            int groupIndex = 2;
918
            Pattern pattern = Pattern.compile(patternStr);
919

    
920
            String abbrevCache = book.getAbbrevTitleCache();
921
            String titleCache = book.getTitleCache();
922
            String vol = null;
923
            String volFull = null;
924
            String abbrev = book.getAbbrevTitle();
925
            if (isNotBlank(abbrev)){
926
                Matcher matcher = pattern.matcher(abbrev);
927
                if (matcher.matches()){
928
                    vol = matcher.group(groupIndex);
929
                    abbrev = matcher.group(1);
930
                }
931
            }
932

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

    
977
    /**
978
     * @param book
979
     * @return
980
     */
981
    private boolean isExtractBookVolumeCandidate(Reference book) {
982
        if (isNotBlank(book.getVolume()) || isNotBlank(book.getEdition()) || isNotBlank(book.getSeriesPart())){
983
            return false;
984
        }
985
        if (!checkExtractBookVolumeTitle(book.getAbbrevTitle())){
986
            return false;
987
        }
988
        if (!checkExtractBookVolumeTitle(book.getTitle())){
989
            return false;
990
        }
991
        return true;
992
    }
993

    
994
    /**
995
     * @param abbrevTitle
996
     * @return
997
     */
998
    private boolean checkExtractBookVolumeTitle(String title) {
999
        if (title == null){
1000
            return true;
1001
        }
1002
        if (title.contains(",") || title.contains("ed.") || title.contains("Ed.")|| title.contains("Suppl")
1003
                || title.contains("Ser.")|| title.contains("ser.")) {
1004
            return false;
1005
        }
1006
        return true;
1007
    }
1008

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

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

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

    
1042
	private Reference makeThesis(Map<String, Object> valueMap){
1043
		if (logger.isDebugEnabled()){logger.debug("RefType 'Thesis'");}
1044
		Reference thesis = ReferenceFactory.newThesis();
1045
		makeStandardMapper(valueMap, thesis, null);
1046
		return thesis;
1047
	}
1048

    
1049

    
1050
	private Reference makeJournalVolume(Map<String, Object> valueMap){
1051
		if (logger.isDebugEnabled()){logger.debug("RefType 'JournalVolume'");}
1052
		//Proceedings proceedings = Proceedings.NewInstance();
1053
		Reference journalVolume = ReferenceFactory.newGeneric();
1054
		makeStandardMapper(valueMap, journalVolume, null);
1055
		logger.warn("Journal volumes not yet implemented. Generic created instead but with errors");
1056
		return journalVolume;
1057
	}
1058

    
1059
	private boolean makeStandardMapper(Map<String, Object> valueMap, Reference ref){
1060
		return makeStandardMapper(valueMap, ref, null);
1061
	}
1062

    
1063

    
1064
	private boolean makeStandardMapper(Map<String, Object> valueMap, CdmBase cdmBase, Set<String> omitAttributes){
1065
		boolean result = true;
1066
		for (CdmAttributeMapperBase mapper : classMappers){
1067
			if (mapper instanceof CdmSingleAttributeMapperBase){
1068
				result &= makeStandardSingleMapper(valueMap, cdmBase, (CdmSingleAttributeMapperBase)mapper, omitAttributes);
1069
			}else if (mapper instanceof CdmOneToManyMapper){
1070
				result &= makeMultipleValueAddMapper(valueMap, cdmBase, (CdmOneToManyMapper)mapper, omitAttributes);
1071
			}else{
1072
				logger.error("Unknown mapper type");
1073
				result = false;
1074
			}
1075
		}
1076
		return result;
1077
	}
1078

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

    
1109

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

    
1126
		result &= ImportHelper.addMultipleValues(sourceValues, cdmBase, destinationAttribute, classes, NO_OVERWRITE, OBLIGATORY);
1127
		return result;
1128
	}
1129

    
1130

    
1131
	private TeamOrPersonBase<?> getAuthorship(BerlinModelImportState state, String refAuthorString,
1132
	        TeamOrPersonBase<?> nomAuthor, Integer refId){
1133

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

    
1158
			    if (result != author){
1159
                    logger.debug("RefAuthorString author deduplicated " + author);
1160
                }else{
1161
                    if (!importSourceExists(author, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference() )){
1162
                        author.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1163
                    }
1164
                }
1165
			}else{
1166
			    logger.debug("RefAuthor loaded from map");
1167
			}
1168
			result = author;
1169
		}else{
1170
			result = null;
1171
		}
1172

    
1173
		return result;
1174
	}
1175

    
1176

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

    
1207
                if (initials != null){
1208
                    if (member.getInitials() != null){
1209
                        logger.warn("Initials already set: " + refId);
1210
                    }else if (!member.isProtectedTitleCache()){
1211
                        member.setInitials(initials);
1212
                    }else {
1213
                        member.setTitleCache(member.getTitleCache() + ", " + initials, true);
1214
                    }
1215
                }
1216

    
1217
                if (i == refAuthorTeams.length -1 && BerlinModelAuthorTeamImport.isEtAl(member)){
1218
                    team.setHasMoreMembers(true);
1219
                }else{
1220
                    Person dedupMember = deduplicatePersonOrTeam(state, member);
1221
                    if (dedupMember != member){
1222
                        hasDedupMember = true;
1223
                    }else{
1224
                        if (!importSourceExists(member, refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference())){
1225
                            member.addImportSource(refAuthorString, REF_AUTHOR_NAMESPACE, state.getTransactionalSourceReference(), null);
1226
                        }
1227
                    }
1228

    
1229
                    team.addTeamMember(dedupMember);
1230
                }
1231
            }
1232
        }
1233

    
1234
        TeamOrPersonBase<?> result = team;
1235
        if (team.getTeamMembers().size() == 1 && !team.isHasMoreMembers()){
1236
            Person person = team.getTeamMembers().get(0);
1237
            checkPerson(person, refAuthorString, hasDedupMember, refId);
1238
            result = person;
1239
        }else{
1240
            checkTeam(team, refAuthorString, refId);
1241
            result = team;
1242
        }
1243

    
1244
        return result;
1245
    }
1246

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

    
1266
        }else{
1267
            team.setTitleCache(refAuthorString, true);
1268
            logger.warn("Creation of titleCache for team with members did not (fully) work: " + refAuthorString + " <-> " + formatter.getTitleCache(team)+ " : " + refId);
1269
        }
1270

    
1271
    }
1272

    
1273
    /**
1274
     * @param hasDedupMember
1275
     * @param result
1276
     * @return
1277
     */
1278
    private static void checkPerson(Person person, String refAuthorString, boolean hasDedupMember, Integer refId) {
1279
        PersonDefaultCacheStrategy formatter = (PersonDefaultCacheStrategy) person.getCacheStrategy();
1280

    
1281
        String oldTitleCache = person.getTitleCache();
1282
        boolean oldTitleCacheProtected = person.isProtectedTitleCache();
1283

    
1284
        if (! oldTitleCache.equals(refAuthorString)){
1285
            logger.error("Old titleCache does not equal refAuthorString this should not happen. "+ oldTitleCache + " <-> " + refAuthorString + "; refId = " + refId);
1286
        }
1287

    
1288
        boolean protect = true;
1289
        person.setProtectedTitleCache(false);
1290
        if (refAuthorString.equals(formatter.getTitleCache(person))){
1291
            protect = false;
1292
        }else if(formatter.getFullTitle(person).equals(refAuthorString)){
1293
            //.. or teams with initials first
1294
            protect = false;
1295
        }else{
1296
            //keep protected, see below
1297
        }
1298

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

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

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

    
1345
    private <T extends TeamOrPersonBase<?>> T deduplicatePersonOrTeam(BerlinModelImportState state,T author) {
1346
        T result = deduplicationHelper.getExistingAuthor(state, author);
1347
        return result;
1348
    }
1349

    
1350
    private Reference deduplicateReference(BerlinModelImportState state,Reference ref) {
1351
        Reference result = deduplicationHelper.getExistingReference(state, ref);
1352
        return result;
1353
    }
1354

    
1355
    private static Person makePerson(String full, boolean followedByInitial, Integer refId) {
1356
        Person person = Person.NewInstance();
1357
        person.setTitleCache(full, true);
1358
        if (!full.matches(".*[\\s\\.].*")){
1359
            person.setFamilyName(full);
1360
            person.setProtectedTitleCache(false);
1361
        }else{
1362
            parsePerson(person, full, true, followedByInitial);
1363
        }
1364

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

    
1369
        return person;
1370
    }
1371

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

    
1395
    /**
1396
     * @param state
1397
     * @param refAuthorString
1398
     * @param nomAuthor
1399
     * @return
1400
     */
1401
    private static boolean handleSimilarAuthors(BerlinModelImportState state, String refAuthorString,
1402
            TeamOrPersonBase<?> nomAuthor, int refId) {
1403
        String nomTitle = nomAuthor.getTitleCache();
1404

    
1405
        if (refAuthorString.equals(nomAuthor.getNomenclaturalTitle())){
1406
            //nomTitle equal
1407
            return true;
1408
        }else{
1409
            if (refAuthorString.replace(" & ", ", ").equals(nomTitle.replace(" & ", ", "))){
1410
                //nomTitle equal except for "&"
1411
                return true;
1412
            }
1413
            String nomFullTitle = nomAuthor.getFullTitle();
1414
            if (refAuthorString.replace(" & ", ", ").equals(nomFullTitle.replace(" & ", ", "))){
1415
                return true;
1416
            }
1417

    
1418
            if (nomAuthor.isInstanceOf(Person.class)){
1419
                Person person = CdmBase.deproxy(nomAuthor, Person.class);
1420

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

    
1438
                if (refAuthorIsFamilyAuthorOfNomAuthor(state, refAuthorString, person)){
1439
                    return true;
1440
                }
1441

    
1442
                if (firstIsFullNameOfInitialName(state, refAuthorString, person, refId)){
1443
                    return true;
1444
                }
1445
            }
1446

    
1447
        }
1448
        return false;
1449
    }
1450

    
1451
    /**
1452
     * @param state
1453
     * @param refAuthorString
1454
     * @param person
1455
     * @return
1456
     */
1457
    private static boolean refAuthorIsFamilyAuthorOfNomAuthor(BerlinModelImportState state, String refAuthorString,
1458
            Person person) {
1459
        if (refAuthorString.equals(person.getFamilyName())){
1460
            return true;
1461
        }else{
1462
            return false;
1463
        }
1464
    }
1465

    
1466
    /**
1467
     * @param state
1468
     * @param refAuthorString
1469
     * @param nomAuthor
1470
     * @return
1471
     */
1472
    private static boolean firstIsFullNameOfInitialName(BerlinModelImportState state, String fullName,
1473
            Person initialAuthor, int refId) {
1474
        String initialName = initialAuthor.getTitleCache();
1475

    
1476
        String[] fullSplits = fullName.split(",");
1477
        String[] initialSplits = initialName.split(",");
1478

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

    
1503
        return false;
1504
    }
1505

    
1506
    /**
1507
     * @param fullGivenName
1508
     * @return
1509
     */
1510
    private static boolean hasAtLeastOneFullName(String[] fullGivenName) {
1511
        for (String singleName : fullGivenName){
1512
            if (!singleName.endsWith(".") && singleName.length() > 2 && !singleName.matches("(von|van)") ){
1513
                return true;
1514
            }
1515
        }
1516
        return false;
1517
    }
1518

    
1519
    /**
1520
     * @param state
1521
     * @param string
1522
     * @param initialAuthor
1523
     */
1524
    private static void setGivenName(BerlinModelImportState state, String givenName, Person person, int refId) {
1525
        givenName = givenName.trim();
1526
        if(person.getGivenName() == null || person.getGivenName().equals(givenName)){
1527
            person.setGivenName(givenName);
1528
        }else{
1529
            logger.warn("RefAuthor given name and existing given name differ: " + givenName + " <-> " + person.getGivenName() + "; RefId + " + refId);
1530
        }
1531
    }
1532

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

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

    
1589
	@Override
1590
	protected boolean doCheck(BerlinModelImportState state){
1591
		BerlinModelReferenceImportValidator validator = new BerlinModelReferenceImportValidator();
1592
		return validator.validate(state, this);
1593
	}
1594

    
1595
	@Override
1596
	protected boolean isIgnore(BerlinModelImportState state){
1597
		return (state.getConfig().getDoReferences() == IImportConfigurator.DO_REFERENCES.NONE);
1598
	}
1599

    
1600
}
(14-14/22)