Project

General

Profile

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

    
11
import java.util.ArrayList;
12
import java.util.Iterator;
13
import java.util.List;
14
import java.util.Set;
15
import java.util.UUID;
16

    
17
import org.apache.commons.lang.StringUtils;
18
import org.apache.log4j.Logger;
19

    
20
import eu.etaxonomy.cdm.common.CdmUtils;
21
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
22
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
23
import eu.etaxonomy.cdm.model.agent.Team;
24
import eu.etaxonomy.cdm.model.common.Language;
25
import eu.etaxonomy.cdm.model.common.Representation;
26
import eu.etaxonomy.cdm.model.name.HybridRelationship;
27
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
28
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
29
import eu.etaxonomy.cdm.model.name.NonViralName;
30
import eu.etaxonomy.cdm.model.name.Rank;
31
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
32
import eu.etaxonomy.cdm.strategy.TagEnum;
33
import eu.etaxonomy.cdm.strategy.TaggedText;
34
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
35
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
36

    
37

    
38
/**
39
 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName> interface.
40
 * The method actually implements a cache strategy for botanical names so no method has to be overwritten by
41
 * a subclass for botanic names.
42
 * Where differing from this Default BotanicNameCacheStrategy other subclasses should overwrite the existing methods
43
 * e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
44
 * @author a.mueller
45
 */
46
/**
47
 * @author AM
48
 *
49
 * @param <T>
50
 */
51
public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends NameCacheStrategyBase<T> implements INonViralNameCacheStrategy<T> {
52
	private static final Logger logger = Logger.getLogger(NonViralNameDefaultCacheStrategy.class);
53
	
54
	final static UUID uuid = UUID.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
55
	
56
	protected String NameAuthorSeperator = " ";
57
	protected String BasionymStart = "(";
58
	protected String BasionymEnd = ")";
59
	protected String ExAuthorSeperator = " ex ";
60
	protected CharSequence BasionymAuthorCombinationAuthorSeperator = " ";
61
	
62
	@Override
63
	public  UUID getUuid(){
64
		return uuid;
65
	}
66

    
67
	
68
	/**
69
	 * Factory method
70
	 * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy
71
	 */
72
	public static NonViralNameDefaultCacheStrategy NewInstance(){
73
		return new NonViralNameDefaultCacheStrategy();
74
	}
75
	
76
	/**
77
	 * Factory method
78
	 * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy
79
	 */
80
	public static <T extends NonViralName<?>> NonViralNameDefaultCacheStrategy<T> NewInstance(Class<T> clazz){
81
		return new NonViralNameDefaultCacheStrategy<T>();
82
	}
83
	
84
	/**
85
	 * Constructor
86
	 */
87
	protected NonViralNameDefaultCacheStrategy(){
88
		super();
89
	}
90

    
91
/* **************** GETTER / SETTER **************************************/
92
	
93
	/**
94
	 * String that separates the NameCache part from the AuthorCache part
95
	 * @return
96
	 */
97
	public String getNameAuthorSeperator() {
98
		return NameAuthorSeperator;
99
	}
100

    
101

    
102
	public void setNameAuthorSeperator(String nameAuthorSeperator) {
103
		NameAuthorSeperator = nameAuthorSeperator;
104
	}
105

    
106

    
107
	/**
108
	 * String the basionym author part starts with e.g. '('.
109
	 * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
110
	 * @return
111
	 */
112
	public String getBasionymStart() {
113
		return BasionymStart;
114
	}
115

    
116

    
117
	public void setBasionymStart(String basionymStart) {
118
		BasionymStart = basionymStart;
119
	}
120

    
121

    
122
	/**
123
	 * String the basionym author part ends with e.g. ')'.
124
	 * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
125
	 * @return
126
	 */
127
	public String getBasionymEnd() {
128
		return BasionymEnd;
129
	}
130

    
131

    
132
	public void setBasionymEnd(String basionymEnd) {
133
		BasionymEnd = basionymEnd;
134
	}
135

    
136

    
137
	/**
138
	 * String to seperate ex author from author.
139
	 * @return
140
	 */
141
	public String getExAuthorSeperator() {
142
		return ExAuthorSeperator;
143
	}
144

    
145

    
146
	public void setExAuthorSeperator(String exAuthorSeperator) {
147
		ExAuthorSeperator = exAuthorSeperator;
148
	}
149

    
150

    
151
	/**
152
	 * String that seperates the basionym/original_combination author part from the combination author part
153
	 * @return
154
	 */
155
	public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
156
		return BasionymAuthorCombinationAuthorSeperator;
157
	}
158

    
159

    
160
	public void setBasionymAuthorCombinationAuthorSeperator(
161
			CharSequence basionymAuthorCombinationAuthorSeperator) {
162
		BasionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
163
	}
164

    
165
	
166
//** *****************************************************************************************/
167

    
168
	/* (non-Javadoc)
169
	 * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTitleCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)
170
	 */
171
	@Override
172
	public String getTitleCache(T nonViralName) {
173
		List<TaggedText> tags = getTaggedTitle(nonViralName);
174
		if (tags == null){
175
			return null;
176
		}else{
177
			String result = createString(tags);
178
			return result;
179
		}
180
	}
181

    
182
	
183
	
184
	public String getTitleCache_OLD(T nonViralName) {
185
		if (nonViralName == null){
186
			return null;
187
		}
188
		
189
		if (nonViralName.isProtectedTitleCache()){
190
			return nonViralName.getTitleCache();
191
		}
192
		String result = "";
193
		if (nonViralName.isHybridFormula()){
194
			//hybrid formula
195
			result = null;
196
			String hybridSeparator = " " + NonViralNameParserImplRegExBase.hybridSign + " ";
197
			List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
198
			for (HybridRelationship rel: rels){
199
				result = CdmUtils.concat(hybridSeparator, result, rel.getParentName().getTitleCache()).trim();
200
			}
201
			return result;
202
		}else if (nonViralName.isAutonym()){
203
			//Autonym
204
			result = handleAutonym(nonViralName);
205
		}else{ //not Autonym
206
			String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));
207
			if (nameIncludesAuthorship(nonViralName)){
208
				String authorCache = CdmUtils.Nz(getAuthorshipCache(nonViralName));
209
				result = CdmUtils.concat(NameAuthorSeperator, nameCache, authorCache);
210
			}else{
211
				result = nameCache;
212
			}
213
		}
214
		return result;
215
	}
216

    
217

    
218
	/**
219
	 * Creates a string from tagged text.
220
	 * @param tags
221
	 * @return
222
	 */
223
	private String createString(List<TaggedText> tags) {
224
		StringBuffer result = new StringBuffer();
225
		
226
		boolean isSeparator;
227
		boolean wasSeparator = true;  //true for start tag
228
		for (TaggedText tag: tags){
229
			isSeparator = tag.getType().equals(TagEnum.separator);
230
			if (! wasSeparator && ! isSeparator ){
231
				result.append(" ");
232
			}
233
			result.append(tag.getText());
234
			wasSeparator = isSeparator;
235
		}
236
		return result.toString();
237
	}
238

    
239

    
240
	/**
241
	 * @param nonViralName
242
	 * @param speciesPart
243
	 * @return
244
	 */
245
	private List<TaggedText> handleTaggedAutonym(T nonViralName) {
246
		
247
		//species part
248
		List<TaggedText> tags = getSpeciesTaggedNameCache(nonViralName);
249
		
250
		//author
251
		//TODO should this include basionym authors and ex authors
252
		INomenclaturalAuthor author = nonViralName.getCombinationAuthorTeam();
253
		String authorPart = "";
254
		if (author != null){
255
			authorPart = CdmUtils.Nz(author.getNomenclaturalTitle());
256
		}
257
		INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();
258
		String basAuthorPart = "";
259
		if (basAuthor != null){
260
			basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());
261
		}
262
		if (! "".equals(basAuthorPart)){
263
			authorPart = "("+ basAuthorPart +") " + authorPart;
264
		}
265
		if (StringUtils.isNotBlank(authorPart)){
266
			tags.add(new TaggedText(TagEnum.authors, authorPart));
267
		}
268
		
269
		
270
		//infra species marker
271
		if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
272
			//TODO handle exception
273
			logger.warn("Rank for autonym does not exist or is not lower than species !!");
274
		}else{
275
			String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
276
			if (StringUtils.isNotBlank(infraSpeciesMarker)){
277
				tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
278
			}
279
		}
280
		
281
		//infra species
282
		String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim().replace("null", "");
283
		if (StringUtils.isNotBlank(infraSpeciesPart)){
284
			tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
285
		}
286
		
287
		return tags;
288
	}
289
	
290
	/**
291
	 * @param nonViralName
292
	 * @param speciesPart
293
	 * @return
294
	 */
295
	private String handleAutonym(T nonViralName) {
296
		String result;
297
		String speciesPart = getSpeciesNameCache(nonViralName);
298
		//TODO should this include basionym authors and ex authors
299
		INomenclaturalAuthor author = nonViralName.getCombinationAuthorTeam();
300
		String authorPart = "";
301
		if (author != null){
302
			authorPart = CdmUtils.Nz(author.getNomenclaturalTitle());
303
		}
304
		INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();
305
		String basAuthorPart = "";
306
		if (basAuthor != null){
307
			basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());
308
		}
309
		if (! "".equals(basAuthorPart)){
310
			authorPart = "("+ basAuthorPart +") " + authorPart;
311
		}
312
		String infraSpeciesPart = (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()));
313

    
314
		String infraSpeciesSeparator = "";
315
		if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
316
			//TODO handle exception
317
			logger.warn("Rank for autonym does not exist or is not lower than species !!");
318
		}else{
319
			infraSpeciesSeparator = nonViralName.getRank().getAbbreviation();
320
		}
321
		
322
		result = CdmUtils.concat(" ", new String[]{speciesPart, authorPart, infraSpeciesSeparator, infraSpeciesPart});
323
		result = result.trim().replace("null", "");
324
		return result;
325
	}
326
	
327
	protected boolean nameIncludesAuthorship(NonViralName<?> nonViralName){
328
		Rank rank = nonViralName.getRank();
329
		if (rank != null && rank.isSpeciesAggregate()){
330
			return false;
331
		}else{
332
			return true;
333
		}
334
	}
335
	
336
	/* (non-Javadoc)
337
	 * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getFullTitleCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)
338
	 */
339
	@Override
340
	public String getFullTitleCache(T nonViralName) {
341
		List<TaggedText> tags = getTaggedFullTitle(nonViralName);
342
		if (tags == null){
343
			return null;
344
		}else{
345
			String result = createString(tags);
346
			return result;
347
		}
348
	}
349
	
350
	
351
	public String getFullTitleCache_OLD(T nonViralName) {
352
		//null
353
		if (nonViralName == null){
354
			return null;
355
		}
356
		//full title cache
357
		if (nonViralName.isProtectedFullTitleCache() == true) {
358
			return nonViralName.getFullTitleCache();
359
		}
360
		
361
		String result = "";
362
		//title cache
363
		String titleCache = nonViralName.getTitleCache();   // OLD: getTitleCache(nonViralName);
364
		
365
		String microReference = nonViralName.getNomenclaturalMicroReference();
366
		INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
367
		String referenceBaseCache = null;
368
		if (ref != null){
369
			INomenclaturalReference nomenclaturalReference = HibernateProxyHelper.deproxy(ref, INomenclaturalReference.class);
370
			nomenclaturalReference.setCacheStrategy(nomenclaturalReference.getType().getCacheStrategy());
371
			referenceBaseCache = nomenclaturalReference.getNomenclaturalCitation(microReference);
372
		}
373
		
374
		//make nomenclatural status
375
		String ncStatusCache = "";
376
		Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
377
		Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
378
		while (iterator.hasNext()) {
379
			NomenclaturalStatus ncStatus = (NomenclaturalStatus)iterator.next();
380
			// since the NewInstance method of nomencatural status allows null as parameter
381
			// we have to check for null values here
382
			String suffix = "not defined";
383
			if(ncStatus.getType() != null){
384
				NomenclaturalStatusType statusType =  ncStatus.getType();
385
				Language lang = Language.LATIN();
386
				Representation repr = statusType.getRepresentation(lang);
387
				if (repr != null){
388
					suffix = repr.getAbbreviatedLabel();
389
				}else{
390
					String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
391
					logger.warn(message);
392
					throw new IllegalStateException(message);
393
				}
394
			}else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){
395
				suffix = ncStatus.getRuleConsidered();
396
			}
397
			ncStatusCache = ", " + suffix;
398
		}
399
		String refConcat = " ";
400
		if (referenceBaseCache != null && ! referenceBaseCache.trim().startsWith("in ")){
401
			refConcat = ", ";
402
		}
403
		result = CdmUtils.concat(refConcat, titleCache, referenceBaseCache);
404
		result = CdmUtils.concat("", result, ncStatusCache);
405
		return result;
406
	}
407
	
408

    
409
	
410
	/**
411
	 * Generates and returns the "name cache" (only scientific name without author teams and year).
412
	 * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy#getNameCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)
413
	 */
414
	@Override
415
	public String getNameCache(T nonViralName) {
416
		List<TaggedText> tags = getNameTags(nonViralName);
417
		if (tags == null){
418
			return null;
419
		}else{
420
			String result = createString(tags);
421
			return result;
422
		}
423
	}
424
	
425
	public String getNameCache_OLD(T nonViralName) {
426
		if (nonViralName == null){
427
			return null;
428
		}
429
		String result;
430
		Rank rank = nonViralName.getRank();
431
		
432
		if (nonViralName.isProtectedNameCache()){
433
			result = nonViralName.getNameCache();
434
		}else if (rank == null){
435
			result = getRanklessNameCache(nonViralName);
436
//		}else if (nonViralName.isInfragenericUnranked()){
437
//			result = getUnrankedInfragenericNameCache(nonViralName);
438
		}else if (rank.isInfraSpecific()){
439
			result = getInfraSpeciesNameCache(nonViralName);
440
		}else if (rank.isSpecies()){
441
			result = getSpeciesNameCache(nonViralName);
442
		}else if (rank.isInfraGeneric()){
443
			result = getInfraGenusNameCache(nonViralName);
444
		}else if (rank.isGenus()){
445
			result = getGenusOrUninomialNameCache(nonViralName);
446
		}else if (rank.isSupraGeneric()){
447
			result = getGenusOrUninomialNameCache(nonViralName);
448
		}else{ 
449
			logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
450
			result = "";
451
		}
452
		return result;
453
	}
454

    
455

    
456
	private String getUnrankedInfragenericNameCache(T nonViralName) {
457
		String result;
458
		Rank rank = nonViralName.getRank();
459
		if (rank.isSpeciesAggregate()){
460
			return getSpeciesAggregateCache(nonViralName);
461
		}
462
		String infraGenericMarker = rank.getAbbreviation();
463
		result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
464
		result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");
465
		result = addAppendedPhrase(result, nonViralName).trim();
466
		return result; 
467
	}
468

    
469

    
470
	/* (non-Javadoc)
471
	 * @see eu.etaxonomy.cdm.strategy.cache.INonViralNameCacheStrategy#getAuthorCache(eu.etaxonomy.cdm.model.name.NonViralName)
472
	 */
473
	public String getAuthorshipCache(T nonViralName) {
474
		if (nonViralName == null){
475
			return null;
476
		}
477
		//cache protected
478
		if (nonViralName.isProtectedAuthorshipCache() == true) {
479
			return nonViralName.getAuthorshipCache();
480
		}
481
		return getNonCacheAuthorshipCache(nonViralName);
482

    
483
	}
484
	
485
	/**
486
	 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorshipfield.
487
	 * @throws NullPointerException if nonViralName is null.
488
	 * @param nonViralName
489
	 * @return
490
	 */
491
	protected String getNonCacheAuthorshipCache(T nonViralName){
492
		String result = "";
493
		INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorTeam();
494
		INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorTeam();
495
		INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorTeam();
496
		INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorTeam();
497
		String basionymPart = "";
498
		String authorPart = "";
499
		//basionym
500
		if (basionymAuthor != null || exBasionymAuthor != null){
501
			basionymPart = BasionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + BasionymEnd;
502
		}
503
		if (combinationAuthor != null || exCombinationAuthor != null){
504
			authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
505
		}
506
		result = CdmUtils.concat(BasionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
507
		return result;
508
	}
509
	
510
	/**
511
	 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on combination authors
512
	 * as well as on basionym/orginal combination authors.
513
	 * @param author the author
514
	 * @param exAuthor the ex-author
515
	 * @return
516
	 */
517
	protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
518
		String result = "";
519
		String authorString = "";
520
		String exAuthorString = "";
521
		if (author != null){
522
			authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
523
		}
524
		if (exAuthor != null){
525
			exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
526
		}
527
		if (exAuthorString.length() > 0 ){
528
			exAuthorString = exAuthorString + ExAuthorSeperator;
529
		}
530
		result = exAuthorString + authorString;
531
		return result;
532
 
533
	}
534
	
535
// ************* TAGGED NAME ***************************************/	
536
	
537
	/* (non-Javadoc)
538
	 * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTaggedTitle(eu.etaxonomy.cdm.model.name.TaxonNameBase)
539
	 */
540
	@Override
541
	public List<TaggedText> getTaggedFullTitle(T nonViralName) {
542
		List<TaggedText> tags = new ArrayList<TaggedText>();
543
		
544
		//null
545
		if (nonViralName == null){
546
			return null;
547
		}
548
		
549
		//protected full title cache
550
		if (nonViralName.isProtectedFullTitleCache()){
551
			tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));
552
			return tags;
553
		}
554
		
555
		//title cache
556
//		String titleCache = nonViralName.getTitleCache();
557
		List<TaggedText> titleTags = getTaggedTitle(nonViralName);
558
		tags.addAll(titleTags);
559
		
560
		
561
		//reference
562
		String microReference = nonViralName.getNomenclaturalMicroReference();
563
		INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
564
		String referenceBaseCache = null;
565
		if (ref != null){
566
			INomenclaturalReference nomenclaturalReference = HibernateProxyHelper.deproxy(ref, INomenclaturalReference.class);
567
			nomenclaturalReference.setCacheStrategy(nomenclaturalReference.getType().getCacheStrategy());
568
			referenceBaseCache = nomenclaturalReference.getNomenclaturalCitation(microReference);
569
		}
570
			//add to tags
571
		if (StringUtils.isNotBlank(referenceBaseCache)){
572
			if (! referenceBaseCache.trim().startsWith("in ")){
573
				String refConcat = ", ";
574
				tags.add(new TaggedText(TagEnum.separator, refConcat));
575
			}
576
			tags.add(new TaggedText(TagEnum.reference, referenceBaseCache));
577
		}
578
		
579
		//nomenclatural status
580
		Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
581
		Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
582
		List<TaggedText> nomStatusTags = new ArrayList<TaggedText>();
583
		while (iterator.hasNext()) {
584
			NomenclaturalStatus ncStatus = (NomenclaturalStatus)iterator.next();
585
			// since the NewInstance method of nomencatural status allows null as parameter
586
			// we have to check for null values here
587
			String suffix = "not defined";
588
			if(ncStatus.getType() != null){
589
				NomenclaturalStatusType statusType =  ncStatus.getType();
590
				Language lang = Language.LATIN();
591
				Representation repr = statusType.getRepresentation(lang);
592
				if (repr != null){
593
					suffix = repr.getAbbreviatedLabel();
594
				}else{
595
					String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
596
					logger.warn(message);
597
					throw new IllegalStateException(message);
598
				}
599
			}else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){
600
				suffix = ncStatus.getRuleConsidered();
601
			}
602
			String statusSeparator = ", ";
603
			nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
604
			nomStatusTags.add(new TaggedText(TagEnum.nomStatus, suffix));
605
		}
606
		tags.addAll(nomStatusTags);
607
		return tags;
608
		
609
	}
610
		
611
	public List<TaggedText> getTaggedTitle(T nonViralName) {
612
		if (nonViralName == null){
613
			return null;
614
		}
615

    
616
		List<TaggedText> tags = new ArrayList<TaggedText>();
617
		
618
		//TODO how to handle protected fullTitleCache here?
619
		
620
		if (nonViralName.isProtectedTitleCache()){
621
			//protected title cache
622
			tags.add(new TaggedText(TagEnum.name, nonViralName.getTitleCache()));
623
			return tags;
624
		}else if (nonViralName.isHybridFormula()){
625
			//hybrid formula
626
			String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
627
			boolean isFirst = true;
628
			List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
629
			for (HybridRelationship rel: rels){
630
				if (! isFirst){
631
					tags.add(new TaggedText(TagEnum.separator, hybridSeparator));
632
				}
633
				tags.add(new TaggedText(TagEnum.name, hybridSeparator));
634
				isFirst = false;
635
				tags.addAll(getTaggedTitle((T)rel.getParentName()));
636
			}
637
			return tags;
638
		}else if (nonViralName.isAutonym()){
639
			//Autonym
640
			tags.addAll(handleTaggedAutonym(nonViralName));
641
		}else{ //not Autonym
642
//			String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));
643
			List<TaggedText> nameTags = getNameTags(nonViralName);
644
			tags.addAll(nameTags);
645
			if (nameIncludesAuthorship(nonViralName)){
646
				String authorCache = getAuthorshipCache(nonViralName);
647
				if (StringUtils.isNotBlank(authorCache)){
648
					tags.add(new TaggedText(TagEnum.authors, authorCache));
649
				}
650
			}
651
		}
652
		return tags;
653

    
654
	}
655
	
656
	
657
	/**
658
	 * @param nonViralName
659
	 * @return
660
	 */
661
	private List<TaggedText> getNameTags(T nonViralName) {
662
		if (nonViralName == null){
663
			return null;
664
		}
665
		List<TaggedText> tags = new ArrayList<TaggedText>();
666
		Rank rank = nonViralName.getRank();
667
		
668
		if (nonViralName.isProtectedNameCache()){
669
			tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
670
		}else if (rank == null){
671
			tags = getRanklessTaggedNameCache(nonViralName);
672
//		}else if (nonViralName.isInfragenericUnranked()){
673
//			result = getUnrankedInfragenericNameCache(nonViralName);
674
		}else if (rank.isInfraSpecific()){
675
			tags = getInfraSpeciesTaggedNameCache(nonViralName);
676
		}else if (rank.isSpecies()){
677
			tags = getSpeciesTaggedNameCache(nonViralName);
678
		}else if (rank.isInfraGeneric()){
679
			tags = getInfraGenusTaggedNameCache(nonViralName);
680
		}else if (rank.isGenus()){
681
			tags = getGenusOrUninomialTaggedNameCache(nonViralName);
682
		}else if (rank.isSupraGeneric()){
683
			tags = getGenusOrUninomialTaggedNameCache(nonViralName);
684
		}else{ 
685
			logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
686
		}
687
		//TODO handle appended phrase here instead of different places, check first if this is true for all
688
		//cases
689
		
690
		return tags;
691

    
692
	}
693

    
694

    
695
	//Old: may be replaced once getTagged(Full)Title is fully tested
696
	/* (non-Javadoc)
697
	 * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getTaggedName(eu.etaxonomy.cdm.model.common.CdmBase)
698
	 */
699
	@Override
700
	public List<Object> getTaggedName(T nonViralName) {
701
		List<Object> tags = new ArrayList<Object>();
702
		
703
		if (nonViralName.isProtectedNameCache() ||
704
				nonViralName.isProtectedAuthorshipCache() ||
705
				nonViralName.isProtectedFullTitleCache() ||
706
				nonViralName.isProtectedTitleCache()){
707
			tags.add(nonViralName.getTitleCache());
708
			return tags;
709
		}
710
		
711
		// Why does it make sense to add the nameCache in case of non existing genusOrUninomial?
712
//		if (nonViralName.getGenusOrUninomial() == null){
713
//			tags.add(nonViralName.getNameCache());
714
//		}else{
715
		
716
		if (nonViralName.getGenusOrUninomial() != null) {
717
			tags.add(nonViralName.getGenusOrUninomial());
718
		}
719
		if (nonViralName.isSpecies() || nonViralName.isInfraSpecific()){
720
			tags.add(nonViralName.getSpecificEpithet());			
721
		}
722
		
723
		// No autonym 
724
		if (nonViralName.isInfraSpecific() && ! nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){
725
			tags.add(nonViralName.getRank());			
726
			tags.add(nonViralName.getInfraSpecificEpithet());			
727
		}
728
		
729
		if (nonViralName.isInfraGeneric()){
730
			//TODO choose right strategy or generic approach?
731
			// --- strategy 1 --- 
732
					
733
			if (nonViralName.getRank().isSpeciesAggregate()){
734
				tags.add(nonViralName.getSpecificEpithet());
735
				tags.add(getSpeciesAggregateEpithet(nonViralName));
736
			}else{
737
				tags.add(nonViralName.getRank());	
738
				tags.add(nonViralName.getInfraGenericEpithet());	
739
			}
740
			// --- strategy 2 --- 
741
//			tags.add('('+nvn.getInfraGenericEpithet()+')');	
742
		}
743
		Team authorTeam = Team.NewInstance();
744
		authorTeam.setProtectedTitleCache(true);
745
		authorTeam.setTitleCache(nonViralName.getAuthorshipCache(), true);
746
		tags.add(authorTeam);
747
		
748
		// Name is an autonym. Rank and infraspecific epitheton follow the author
749
		if (nonViralName.isInfraSpecific() && nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){
750
			tags.add(nonViralName.getRank());			
751
			tags.add(nonViralName.getInfraSpecificEpithet());			
752
		}
753
		
754
		if(! "".equals(nonViralName.getAppendedPhrase())&& (nonViralName.getAppendedPhrase() != null)){
755
			tags.add(nonViralName.getAppendedPhrase());
756
		}
757
		
758
		return tags;
759
	}
760
	
761

    
762
//***************************** PRIVATES ***************************************/
763
		
764
	protected List<TaggedText> getRanklessTaggedNameCache(NonViralName<?> nonViralName){
765
		List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
766
		String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");
767
		if (StringUtils.isNotBlank(speciesEpi)){
768
			tags.add(new TaggedText(TagEnum.name, speciesEpi));
769
		}
770
		
771
		String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim().replace("null", "");
772
		if (StringUtils.isNotBlank(infraSpeciesEpi)){
773
			tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
774
		}
775
		
776
		//result += " (rankless)";
777
		addAppendedTaggedPhrase(tags, nonViralName);
778
		return tags;			
779
	}
780
	
781
	protected String getRanklessNameCache(NonViralName<?> nonViralName){
782
		String result = "";
783
		result = (result + (CdmUtils.Nz(nonViralName.getGenusOrUninomial()))).trim().replace("null", "");
784
		result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet())).trim();
785
		result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim();
786
		result = result.trim().replace("null", "");
787
		//result += " (rankless)";
788
		result = addAppendedPhrase(result, nonViralName);
789
		return result;			
790
	}
791

    
792
		
793
	protected List<TaggedText> getGenusOrUninomialTaggedNameCache(NonViralName<?> nonViralName){
794
		List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
795
		addAppendedTaggedPhrase(tags, nonViralName);
796
		return tags;
797
	}
798

    
799
	protected String getGenusOrUninomialNameCache(NonViralName<?> nonViralName){
800
		String result;
801
		result = getUninomialPart(nonViralName);
802
		result = addAppendedPhrase(result, nonViralName).trim();
803
		return result;
804
	}
805

    
806

    
807
	private List<TaggedText> getUninomialTaggedPart(NonViralName<?> nonViralName) {
808
		List<TaggedText> tags = new ArrayList<TaggedText>();
809
		
810
		if (nonViralName.isMonomHybrid()){
811
			addHybridPrefix(tags);
812
		}
813
		
814
		String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim().replace("null", "");
815
		if (StringUtils.isNotBlank(uninomial)){
816
			tags.add(new TaggedText(TagEnum.name, uninomial));
817
		}
818
			
819
		return tags;
820
	}
821
	
822
	private String getUninomialPart(NonViralName<?> nonViralName) {
823
		String result;
824
		result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
825
		if (nonViralName.isMonomHybrid()){
826
			result = NonViralNameParserImplRegExBase.hybridSign + result; 
827
		}
828
		return result;
829
	}
830
	
831
	
832
	protected List<TaggedText> getInfraGenusTaggedNameCache(NonViralName<?> nonViralName){
833
		Rank rank = nonViralName.getRank();
834
		if (rank.isSpeciesAggregate()){
835
			return getSpeciesAggregateTaggedCache(nonViralName);
836
		}
837
		
838
		//genus
839
		List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
840
		
841
		//marker
842
		String infraGenericMarker = "'unhandled infrageneric rank'";
843
		if (rank != null){
844
			try {
845
				infraGenericMarker = rank.getInfraGenericMarker();
846
			} catch (UnknownCdmTypeException e) {
847
				infraGenericMarker = "'unhandled infrageneric rank'";
848
			}
849
		}
850
		tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
851
		
852
		
853
		String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim().replace("null", "");
854
		if (StringUtils.isNotBlank(infraGenEpi)){
855
			tags.add(new TaggedText(TagEnum.name, infraGenEpi));
856
		}
857
		
858
		addAppendedTaggedPhrase(tags, nonViralName);
859
		return tags;
860
	}
861
	
862
	protected String getInfraGenusNameCache(NonViralName<?> nonViralName){
863
		String result;
864
		Rank rank = nonViralName.getRank();
865
		if (rank.isSpeciesAggregate()){
866
			return getSpeciesAggregateCache(nonViralName);
867
		}
868
		String infraGenericMarker = "'unhandled infrageneric rank'";
869
		if (rank != null){
870
			try {
871
				infraGenericMarker = rank.getInfraGenericMarker();
872
			} catch (UnknownCdmTypeException e) {
873
				infraGenericMarker = "'unhandled infrageneric rank'";
874
			}
875
		}
876
		result = getUninomialPart(nonViralName);
877
		result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");
878
		result = addAppendedPhrase(result, nonViralName).trim();
879
		return result;
880
	}
881

    
882
//		aggr.|agg.|group
883
	protected List<TaggedText> getSpeciesAggregateTaggedCache(NonViralName<?> nonViralName){
884
		List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
885
		
886
		addSpeciesAggregateTaggedEpithet(tags, nonViralName);
887
		addAppendedTaggedPhrase(tags, nonViralName);
888
		return tags;
889
	}
890
	
891
	private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, NonViralName<?> nonViralName) {
892
		String marker;
893
		try {
894
			marker = nonViralName.getRank().getInfraGenericMarker();
895
		} catch (UnknownCdmTypeException e) {
896
			marker = "'unknown aggregat type'";
897
		}
898
		if (StringUtils.isNotBlank(marker)){
899
			tags.add(new TaggedText(TagEnum.rank, marker));
900
		}
901
	}
902
	
903
//		aggr.|agg.|group
904
	protected String getSpeciesAggregateCache(NonViralName<?> nonViralName){
905
		String result = getGenusAndSpeciesPart(nonViralName);
906
		
907
		result += " " + getSpeciesAggregateEpithet(nonViralName);
908
		result = addAppendedPhrase(result, nonViralName).trim();
909
		return result;
910
	}
911
	
912
	private String getSpeciesAggregateEpithet(NonViralName<?> nonViralName) {
913
		String marker;
914
		try {
915
			marker = nonViralName.getRank().getInfraGenericMarker();
916
		} catch (UnknownCdmTypeException e) {
917
			marker = "'unknown aggregat type'";
918
		}
919
		return marker;
920
	}
921

    
922
	
923
	protected List<TaggedText> getSpeciesTaggedNameCache(NonViralName<?> nonViralName){
924
		List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
925
		addAppendedTaggedPhrase(tags, nonViralName);
926
		return tags;
927
	}
928
	
929
	protected String getSpeciesNameCache(NonViralName<?> nonViralName){
930
		String result = getGenusAndSpeciesPart(nonViralName);
931
		result = addAppendedPhrase(result, nonViralName).trim();
932
		result = result.replace("\\s\\", " ");
933
		return result;
934
	}
935

    
936
	protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName){
937
		return getInfraSpeciesTaggedNameCache(nonViralName, true);
938
	}
939
	
940
	protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName, boolean includeMarker){
941
		List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
942
		if (includeMarker){ 
943
			String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
944
			if (StringUtils.isNotBlank(marker)){
945
				tags.add(new TaggedText(TagEnum.rank, marker));
946
			}
947
		}
948
		String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
949
		if (nonViralName.isTrinomHybrid()){
950
			addHybridPrefix(tags);
951
		}
952
		
953
		infrSpecEpi = infrSpecEpi.trim().replace("null", "");
954
		if (StringUtils.isNotBlank(infrSpecEpi)){
955
			tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
956
		}
957
		
958
		addAppendedTaggedPhrase(tags, nonViralName);
959
		return tags;
960
	}
961

    
962

    
963
	/**
964
	 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
965
	 * @param tags
966
	 */
967
	private void addHybridPrefix(List<TaggedText> tags) {
968
		tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
969
		tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
970
	}
971
	
972
	protected String getInfraSpeciesNameCache(NonViralName<?> nonViralName){
973
		return getInfraSpeciesNameCache(nonViralName, true);
974
	}
975

    
976
	
977
	protected String getInfraSpeciesNameCache(NonViralName<?> nonViralName, boolean includeMarker){
978
		String result = getGenusAndSpeciesPart(nonViralName);
979
		if (includeMarker){ 
980
			result += " " + (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
981
		}
982
		String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
983
		if (nonViralName.isTrinomHybrid()){
984
			infrSpecEpi = NonViralNameParserImplRegExBase.hybridSign + infrSpecEpi; 
985
		}
986
		result += " " + (infrSpecEpi).trim().replace("null", "");
987
		result = addAppendedPhrase(result, nonViralName).trim();
988
		return result;
989
	}
990

    
991

    
992
	private List<TaggedText> getGenusAndSpeciesTaggedPart(NonViralName<?> nonViralName) {
993
		//Uninomial
994
		List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
995
		
996
		//InfraGenericEpi
997
		boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
998
		if (hasInfraGenericEpi){
999
			String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
1000
			if (nonViralName.isBinomHybrid()){
1001
//					addHybridPrefix(tags);  FIXME hybridSign should be tag, but then we need to handle "(" ")" differently.
1002
				infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi;
1003
			}
1004
			infrGenEpi = "(" + infrGenEpi + ")";
1005
			tags.add(new TaggedText(TagEnum.name, infrGenEpi));
1006
		}
1007

    
1008
		//Species Epi
1009
		String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");
1010
		if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() || 
1011
				hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
1012
			addHybridPrefix(tags); 
1013
		}
1014
		if (StringUtils.isNotBlank(specEpi)){
1015
			tags.add(new TaggedText(TagEnum.name, specEpi));
1016
		}
1017
		return tags;
1018
	}
1019
	
1020
	private String getGenusAndSpeciesPart(NonViralName<?> nonViralName) {
1021
		String result;
1022
		//Uninomial
1023
		result = getUninomialPart(nonViralName);
1024
		
1025
		//InfraGenericEpi
1026
		boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
1027
		if (hasInfraGenericEpi){
1028
			String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
1029
			if (nonViralName.isBinomHybrid()){
1030
				infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi; 
1031
			}
1032
			result += " (" + infrGenEpi + ")";
1033
		}
1034
		//Species Epi
1035
		String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
1036
		if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() || 
1037
				hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
1038
			specEpi = NonViralNameParserImplRegExBase.hybridSign +  specEpi; 
1039
		}
1040
		result += " " + (specEpi).replace("null", "");
1041
		return result;
1042
	}
1043

    
1044
	
1045
	/**
1046
	 * Adds the tag for the appended phrase if an appended phrase exists
1047
	 * @param tags
1048
	 * @param nonViralName
1049
	 */
1050
	protected void addAppendedTaggedPhrase(List<TaggedText> tags, NonViralName<?> nonViralName){
1051
		String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
1052
		if (StringUtils.isNotEmpty(appendedPhrase)){
1053
			tags.add(new TaggedText(TagEnum.name, appendedPhrase));
1054
		}
1055
	}
1056
	
1057
	protected String addAppendedPhrase(String resultString, NonViralName<?> nonViralName){
1058
		String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
1059
		if (resultString == null){
1060
			return appendedPhrase;
1061
		}else if(appendedPhrase == null || "".equals(appendedPhrase.trim())) {
1062
			return resultString;
1063
		}else if ("".equals(resultString)){
1064
			return resultString + appendedPhrase;
1065
		}else {
1066
			return resultString + " " + appendedPhrase;
1067
		}
1068
	}
1069

    
1070

    
1071
	public String getLastEpithet(T taxonNameBase) {
1072
		Rank rank = taxonNameBase.getRank();
1073
		if(rank.isGenus() || rank.isSupraGeneric()) {
1074
			return taxonNameBase.getGenusOrUninomial();
1075
		} else if(rank.isInfraGeneric()) {
1076
			return taxonNameBase.getInfraGenericEpithet();
1077
		} else if(rank.isSpecies()) {
1078
			return taxonNameBase.getSpecificEpithet();
1079
		} else {
1080
			return taxonNameBase.getInfraSpecificEpithet();
1081
		}
1082
	}
1083
}
(7-7/8)