Project

General

Profile

Download (20.4 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.NomenclaturalStatus;
27
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
28
import eu.etaxonomy.cdm.model.name.NonViralName;
29
import eu.etaxonomy.cdm.model.name.Rank;
30
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
31
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
32

    
33

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

    
63
	
64
	/**
65
	 * Factory method
66
	 * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy
67
	 */
68
	public static NonViralNameDefaultCacheStrategy NewInstance(){
69
		return new NonViralNameDefaultCacheStrategy();
70
	}
71
	
72
	/**
73
	 * Constructor
74
	 */
75
	protected NonViralNameDefaultCacheStrategy(){
76
		super();
77
	}
78

    
79
/* **************** GETTER / SETTER **************************************/
80
	
81
	/**
82
	 * String that separates the NameCache part from the AuthorCache part
83
	 * @return
84
	 */
85
	public String getNameAuthorSeperator() {
86
		return NameAuthorSeperator;
87
	}
88

    
89

    
90
	public void setNameAuthorSeperator(String nameAuthorSeperator) {
91
		NameAuthorSeperator = nameAuthorSeperator;
92
	}
93

    
94

    
95
	/**
96
	 * String the basionym author part starts with e.g. '('.
97
	 * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
98
	 * @return
99
	 */
100
	public String getBasionymStart() {
101
		return BasionymStart;
102
	}
103

    
104

    
105
	public void setBasionymStart(String basionymStart) {
106
		BasionymStart = basionymStart;
107
	}
108

    
109

    
110
	/**
111
	 * String the basionym author part ends with e.g. ')'.
112
	 * This should correspond with the {@link NonViralNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
113
	 * @return
114
	 */
115
	public String getBasionymEnd() {
116
		return BasionymEnd;
117
	}
118

    
119

    
120
	public void setBasionymEnd(String basionymEnd) {
121
		BasionymEnd = basionymEnd;
122
	}
123

    
124

    
125
	/**
126
	 * String to seperate ex author from author.
127
	 * @return
128
	 */
129
	public String getExAuthorSeperator() {
130
		return ExAuthorSeperator;
131
	}
132

    
133

    
134
	public void setExAuthorSeperator(String exAuthorSeperator) {
135
		ExAuthorSeperator = exAuthorSeperator;
136
	}
137

    
138

    
139
	/**
140
	 * String that seperates the basionym/original_combination author part from the combination author part
141
	 * @return
142
	 */
143
	public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
144
		return BasionymAuthorCombinationAuthorSeperator;
145
	}
146

    
147

    
148
	public void setBasionymAuthorCombinationAuthorSeperator(
149
			CharSequence basionymAuthorCombinationAuthorSeperator) {
150
		BasionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
151
	}
152

    
153
	
154
//** *****************************************************************************************/
155
	
156
	
157
	/* (non-Javadoc)
158
	 * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getNameCache()
159
	 */
160
	@Override
161
	public String getTitleCache(T nonViralName) {
162
		if (nonViralName == null){
163
			return null;
164
		}
165
		
166
		if (nonViralName.isProtectedTitleCache()){
167
			return nonViralName.getTitleCache();
168
		}
169
		String result = "";
170
		//Autonym
171
		if (nonViralName.isAutonym()){
172
			result = handleAutonym(nonViralName);
173
		}else{ //not Autonym
174
			String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));
175
			if (nameIncludesAuthorship(nonViralName)){
176
				String authorCache = CdmUtils.Nz(getAuthorshipCache(nonViralName));
177
				result = CdmUtils.concat(NameAuthorSeperator, nameCache, authorCache);
178
			}else{
179
				result = nameCache;
180
			}
181
		}
182
		return result;
183
	}
184

    
185

    
186
	/**
187
	 * @param nonViralName
188
	 * @param speciesPart
189
	 * @return
190
	 */
191
	private String handleAutonym(T nonViralName) {
192
		String result;
193
		String speciesPart = getSpeciesNameCache(nonViralName);
194
		//TODO should this include basionym authors and ex authors
195
		INomenclaturalAuthor author = nonViralName.getCombinationAuthorTeam();
196
		String authorPart = "";
197
		if (author != null){
198
			authorPart = CdmUtils.Nz(author.getNomenclaturalTitle());
199
		}
200
		INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();
201
		String basAuthorPart = "";
202
		if (basAuthor != null){
203
			basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());
204
		}
205
		if (! "".equals(basAuthorPart)){
206
			authorPart = "("+ basAuthorPart +")" + authorPart;
207
		}
208
		String infraSpeciesPart = (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()));
209

    
210
		String infraSpeciesSeparator = "";
211
		if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
212
			//TODO handle exception
213
			logger.warn("Rank for autonym does not exist or is not lower than species !!");
214
		}else{
215
			infraSpeciesSeparator = nonViralName.getRank().getAbbreviation();
216
		}
217
		
218
		result = CdmUtils.concat(" ", new String[]{speciesPart, authorPart, infraSpeciesSeparator, infraSpeciesPart});
219
		result = result.trim().replace("null", "");
220
		return result;
221
	}
222
	
223
	protected boolean nameIncludesAuthorship(NonViralName nonViralName){
224
		Rank rank = nonViralName.getRank();
225
		if (rank != null && rank.isSpeciesAggregate()){
226
			return false;
227
		}else{
228
			return true;
229
		}
230
	}
231
	
232
	
233

    
234
	
235
	
236
	@Override
237
	public String getFullTitleCache(T nonViralName) {
238
		//null
239
		if (nonViralName == null){
240
			return null;
241
		}
242
		//full title cache
243
		if (nonViralName.isProtectedFullTitleCache() == true) {
244
			return nonViralName.getFullTitleCache();
245
		}
246
		
247
		String result = "";
248
		//title cache
249
		String titleCache = nonViralName.getTitleCache();   // OLD: getTitleCache(nonViralName);
250
		
251
		String microReference = nonViralName.getNomenclaturalMicroReference();
252
		INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
253
		String referenceBaseCache = null;
254
		if (ref != null){
255
			INomenclaturalReference nomenclaturalReference = HibernateProxyHelper.deproxy(ref, INomenclaturalReference.class);
256
			nomenclaturalReference.setCacheStrategy(nomenclaturalReference.getType().getCacheStrategy());
257
			referenceBaseCache = nomenclaturalReference.getNomenclaturalCitation(microReference);
258
		}
259
		
260
		//make nomenclatural status
261
		String ncStatusCache = "";
262
		Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
263
		Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
264
		while (iterator.hasNext()) {
265
			NomenclaturalStatus ncStatus = (NomenclaturalStatus)iterator.next();
266
			// since the NewInstance method of nomencatural status allows null as parameter
267
			// we have to check for null values here
268
			String suffix = "not defined";
269
			if(ncStatus.getType() != null){
270
				NomenclaturalStatusType statusType =  ncStatus.getType();
271
				Language lang = Language.LATIN();
272
				Representation repr = statusType.getRepresentation(lang);
273
				if (repr != null){
274
					suffix = repr.getAbbreviatedLabel();
275
				}else{
276
					String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
277
					logger.warn(message);
278
					throw new IllegalStateException(message);
279
				}
280
			}else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){
281
				suffix = ncStatus.getRuleConsidered();
282
			}
283
			ncStatusCache = ", " + suffix;
284
		}
285
		String refConcat = " ";
286
		if (referenceBaseCache != null && ! referenceBaseCache.trim().startsWith("in ")){
287
			refConcat = ", ";
288
		}
289
		result = CdmUtils.concat(refConcat, titleCache, referenceBaseCache);
290
		result = CdmUtils.concat("", result, ncStatusCache);
291
		return result;
292
	}
293
	
294
	
295
	/**
296
	 * Generates and returns the "name cache" (only scientific name without author teams and year).
297
	 * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy#getNameCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)
298
	 */
299
	public String getNameCache(T nonViralName) {
300
		if (nonViralName == null){
301
			return null;
302
		}
303
		String result;
304
		Rank rank = nonViralName.getRank();
305
		
306
		if (nonViralName.isProtectedNameCache()){
307
			result = nonViralName.getNameCache();
308
		}else if (rank == null){
309
			result = getRanklessNameCache(nonViralName);
310
		}else if (nonViralName.isInfragenericUnranked()){
311
			result = getUnrankedInfragenericNameCache(nonViralName);
312
		}else if (rank.isInfraSpecific()){
313
			result = getInfraSpeciesNameCache(nonViralName);
314
		}else if (rank.isSpecies()){
315
			result = getSpeciesNameCache(nonViralName);
316
		}else if (rank.isInfraGeneric()){
317
			result = getInfraGenusNameCache(nonViralName);
318
		}else if (rank.isGenus()){
319
			result = getGenusOrUninomialNameCache(nonViralName);
320
		}else if (rank.isSupraGeneric()){
321
			result = getGenusOrUninomialNameCache(nonViralName);
322
		}else{ 
323
			logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
324
			result = "";
325
		}
326
		return result;
327
	}
328
//	
329
//
330
//	/**
331
//	 * @param nonViralName
332
//	 * @return
333
//	 */
334
//	private boolean isInfragenericUnranked(T nonViralName) {
335
//		Rank rank = nonViralName.getRank();
336
//		if (rank == null || ! rank.equals(Rank.UNRANKED())){
337
//			return false;
338
//		}
339
//		if (StringUtils.isBlank(nonViralName.getSpecificEpithet()) && StringUtils.isBlank(nonViralName.getInfraSpecificEpithet()) ){
340
//			return true;
341
//		}else{
342
//			return false;
343
//		}
344
//	}
345

    
346

    
347
	private String getUnrankedInfragenericNameCache(T nonViralName) {
348
		String result;
349
		Rank rank = nonViralName.getRank();
350
		if (rank.isSpeciesAggregate()){
351
			return getSpeciesAggregateCache(nonViralName);
352
		}
353
		String infraGenericMarker = rank.getAbbreviation();
354
		result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
355
		result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");
356
		result = addAppendedPhrase(result, nonViralName).trim();
357
		return result; 
358
	}
359

    
360

    
361
	/* (non-Javadoc)
362
	 * @see eu.etaxonomy.cdm.strategy.cache.INonViralNameCacheStrategy#getAuthorCache(eu.etaxonomy.cdm.model.name.NonViralName)
363
	 */
364
	public String getAuthorshipCache(T nonViralName) {
365
		if (nonViralName == null){
366
			return null;
367
		}
368
		//cache protected
369
		if (nonViralName.isProtectedAuthorshipCache() == true) {
370
			return nonViralName.getAuthorshipCache();
371
		}
372
		return getNonCacheAuthorshipCache(nonViralName);
373

    
374
	}
375
	
376
	/**
377
	 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorshipfield.
378
	 * @throws NullPointerException if nonViralName is null.
379
	 * @param nonViralName
380
	 * @return
381
	 */
382
	protected String getNonCacheAuthorshipCache(T nonViralName){
383
		String result = "";
384
		INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorTeam();
385
		INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorTeam();
386
		INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorTeam();
387
		INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorTeam();
388
		String basionymPart = "";
389
		String authorPart = "";
390
		//basionym
391
		if (basionymAuthor != null || exBasionymAuthor != null){
392
			basionymPart = BasionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + BasionymEnd;
393
		}
394
		if (combinationAuthor != null || exCombinationAuthor != null){
395
			authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
396
		}
397
		result = CdmUtils.concat(BasionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
398
		return result;
399
	}
400
	
401
	/**
402
	 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on combination authors
403
	 * as well as on basionym/orginal combination authors.
404
	 * @param author the author
405
	 * @param exAuthor the ex-author
406
	 * @return
407
	 */
408
	protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
409
		String result = "";
410
		String authorString = "";
411
		String exAuthorString = "";
412
		if (author != null){
413
			authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
414
		}
415
		if (exAuthor != null){
416
			exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
417
		}
418
		if (exAuthorString.length() > 0 ){
419
			exAuthorString = exAuthorString + ExAuthorSeperator;
420
		}
421
		result = exAuthorString + authorString;
422
		return result;
423
 
424
	}
425
	
426
	
427
	/* (non-Javadoc)
428
	 * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getTaggedName(eu.etaxonomy.cdm.model.common.CdmBase)
429
	 */
430
	@Override
431
	public List<Object> getTaggedName(T nonViralName) {
432
		List<Object> tags = new ArrayList<Object>();
433
		
434
		if (nonViralName.isProtectedNameCache() ||
435
				nonViralName.isProtectedAuthorshipCache() ||
436
				nonViralName.isProtectedFullTitleCache() ||
437
				nonViralName.isProtectedTitleCache()){
438
			tags.add(nonViralName.getTitleCache());
439
			return tags;
440
		}
441
		
442
		// Why does it make sense to add the nameCache in case of non existing genusOrUninomial?
443
//		if (nonViralName.getGenusOrUninomial() == null){
444
//			tags.add(nonViralName.getNameCache());
445
//		}else{
446
		
447
		if (nonViralName.getGenusOrUninomial() != null) {
448
			tags.add(nonViralName.getGenusOrUninomial());
449
		}
450
		if (nonViralName.isSpecies() || nonViralName.isInfraSpecific()){
451
			tags.add(nonViralName.getSpecificEpithet());			
452
		}
453
		
454
		// No autonym 
455
		if (nonViralName.isInfraSpecific() && ! nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){
456
			tags.add(nonViralName.getRank());			
457
			tags.add(nonViralName.getInfraSpecificEpithet());			
458
		}
459
		
460
		if (nonViralName.isInfraGeneric()){
461
			//TODO choose right strategy or generic approach?
462
			// --- strategy 1 --- 
463
					
464
			if (nonViralName.getRank().isSpeciesAggregate()){
465
				tags.add(nonViralName.getSpecificEpithet());
466
				tags.add(getSpeciesAggregateEpithet(nonViralName));
467
			}else{
468
				tags.add(nonViralName.getRank());	
469
				tags.add(nonViralName.getInfraGenericEpithet());	
470
			}
471
			// --- strategy 2 --- 
472
//			tags.add('('+nvn.getInfraGenericEpithet()+')');	
473
		}
474
		Team authorTeam = Team.NewInstance();
475
		authorTeam.setProtectedTitleCache(true);
476
		authorTeam.setTitleCache(nonViralName.getAuthorshipCache(), true);
477
		tags.add(authorTeam);
478
		
479
		// Name is an autonym. Rank and infraspecific epitheton follow the author
480
		if (nonViralName.isInfraSpecific() && nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){
481
			tags.add(nonViralName.getRank());			
482
			tags.add(nonViralName.getInfraSpecificEpithet());			
483
		}
484
		
485
		if(! "".equals(nonViralName.getAppendedPhrase())&& (nonViralName.getAppendedPhrase() != null)){
486
			tags.add(nonViralName.getAppendedPhrase());
487
		}
488
		
489
		return tags;
490
	}
491
	
492

    
493
//***************************** PRIVATES ***************************************/
494
		
495
		protected String getRanklessNameCache(NonViralName nonViralName){
496
			String result = "";
497
			result = (result + (CdmUtils.Nz(nonViralName.getGenusOrUninomial()))).trim().replace("null", "");
498
			result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet())).trim();
499
			result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim();
500
			result = result.trim().replace("null", "");
501
			//result += " (rankless)";
502
			result = addAppendedPhrase(result, nonViralName);
503
			return result;			
504
		}
505
	
506
	
507
		protected String getGenusOrUninomialNameCache(NonViralName nonViralName){
508
			String result;
509
			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
510
			result = addAppendedPhrase(result, nonViralName).trim();
511
			return result;
512
		}
513
		
514
		protected String getInfraGenusNameCache(NonViralName nonViralName){
515
			String result;
516
			Rank rank = nonViralName.getRank();
517
			if (rank.isSpeciesAggregate()){
518
				return getSpeciesAggregateCache(nonViralName);
519
			}
520
			String infraGenericMarker = "'unhandled infrageneric rank'";
521
			if (rank != null){
522
				try {
523
					infraGenericMarker = rank.getInfraGenericMarker();
524
				} catch (UnknownCdmTypeException e) {
525
					infraGenericMarker = "'unhandled infrageneric rank'";
526
				}
527
			}
528
			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
529
			result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");
530
			result = addAppendedPhrase(result, nonViralName).trim();
531
			return result;
532
		}
533

    
534
//		aggr.|agg.|group
535
		protected String getSpeciesAggregateCache(NonViralName nonViralName){
536
			String result = getGenusAndSpeciesPart(nonViralName);
537
			
538
			result += " " + getSpeciesAggregateEpithet(nonViralName);
539
			result = addAppendedPhrase(result, nonViralName).trim();
540
			return result;
541
		}
542
		
543
		private String getSpeciesAggregateEpithet(NonViralName nonViralName) {
544
			String marker;
545
			try {
546
				marker = nonViralName.getRank().getInfraGenericMarker();
547
			} catch (UnknownCdmTypeException e) {
548
				marker = "'unknown aggregat type'";
549
			}
550
			return marker;
551
		}
552
		
553
		protected String getSpeciesNameCache(NonViralName nonViralName){
554
			String result = getGenusAndSpeciesPart(nonViralName);
555
			result = addAppendedPhrase(result, nonViralName).trim();
556
			result = result.replace("\\s\\", " ");
557
			return result;
558
		}
559
		
560
		
561
		protected String getInfraSpeciesNameCache(NonViralName nonViralName){
562
			return getInfraSpeciesNameCache(nonViralName, true);
563
		}
564
		
565
		protected String getInfraSpeciesNameCache(NonViralName nonViralName, boolean includeMarker){
566
			String result = getGenusAndSpeciesPart(nonViralName);
567
			if (includeMarker){ 
568
				result += " " + (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
569
			}
570
			result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim().replace("null", "");
571
			result = addAppendedPhrase(result, nonViralName).trim();
572
			return result;
573
		}
574

    
575

    
576
		private String getGenusAndSpeciesPart(NonViralName nonViralName) {
577
			String result;
578
			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
579
			if (StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet()) ){
580
				result += " (" + nonViralName.getInfraGenericEpithet().trim() + ")";
581
			}
582
			result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim()).replace("null", "");
583
			return result;
584
		}
585

    
586
		
587
		protected String addAppendedPhrase(String resultString, NonViralName nonViralName){
588
			String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
589
			if (resultString == null){
590
				return appendedPhrase;
591
			}else if(appendedPhrase == null || "".equals(appendedPhrase.trim())) {
592
				return resultString;
593
			}else if ("".equals(resultString)){
594
				return resultString + appendedPhrase;
595
			}else {
596
				return resultString + " " + appendedPhrase;
597
			}
598
		}
599

    
600

    
601
		public String getLastEpithet(T taxonNameBase) {
602
			Rank rank = taxonNameBase.getRank();
603
			if(rank.isGenus() || rank.isSupraGeneric()) {
604
				return taxonNameBase.getGenusOrUninomial();
605
			} else if(rank.isInfraGeneric()) {
606
				return taxonNameBase.getInfraGenericEpithet();
607
			} else if(rank.isSpecies()) {
608
				return taxonNameBase.getSpecificEpithet();
609
			} else {
610
				return taxonNameBase.getInfraSpecificEpithet();
611
			}
612
		}
613
}
(7-7/8)