+ @Override
+ public String generateFullTitle(){
+ if (cacheStrategy == null){
+ logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
+ return null;
+ }else{
+ return cacheStrategy.getFullTitleCache(this);
+ }
+ }
+
+ /**
+ * Generates the composed name string of <i>this</i> non viral taxon name without author
+ * strings or year according to the strategy defined in
+ * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy INonViralNameCacheStrategy}.
+ * The result might be stored in {@link #getNameCache() nameCache} if the
+ * flag {@link #isProtectedNameCache() protectedNameCache} is not set.
+ *
+ * @return the string with the composed name of <i>this</i> non viral taxon name without authors or year
+ * @see #getNameCache()
+ */
+ protected String generateNameCache(){
+ if (cacheStrategy == null){
+ logger.warn("No CacheStrategy defined for taxonName: " + this.toString());
+ return null;
+ }else{
+ return cacheStrategy.getNameCache(this);
+ }
+ }
+
+ /**
+ * Returns or generates the nameCache (scientific name
+ * without author strings and year) string for <i>this</i> non viral taxon name. If the
+ * {@link #isProtectedNameCache() protectedNameCache} flag is not set (False)
+ * the string will be generated according to a defined strategy,
+ * otherwise the value of the actual nameCache string will be returned.
+ *
+ * @return the string which identifies <i>this</i> non viral taxon name (without authors or year)
+ * @see #generateNameCache()
+ */
+ @Transient
+ public String getNameCache() {
+ if (protectedNameCache){
+ return this.nameCache;
+ }
+ // is title dirty, i.e. equal NULL?
+ if (nameCache == null){
+ this.nameCache = generateNameCache();
+ }
+ return nameCache;
+ }
+
+ /**
+ * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
+ * Sets the protectedNameCache flag to <code>true</code>.
+ *
+ * @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
+ * @see #getNameCache()
+ */
+ public void setNameCache(String nameCache){
+ setNameCache(nameCache, true);
+ }
+
+ /**
+ * Assigns a nameCache string to <i>this</i> non viral taxon name and protects it from being overwritten.
+ * Sets the protectedNameCache flag to <code>true</code>.
+ *
+ * @param nameCache the string which identifies <i>this</i> non viral taxon name (without authors or year)
+ * @param protectedNameCache if true teh protectedNameCache is set to <code>true</code> or otherwise set to
+ * <code>false</code>
+ * @see #getNameCache()
+ */
+ public void setNameCache(String nameCache, boolean protectedNameCache){
+ this.nameCache = nameCache;
+ this.setProtectedNameCache(protectedNameCache);
+ }
+
+ /**
+ * Returns the boolean value of the flag intended to protect (true)
+ * or not (false) the {@link #getNameCache() nameCache} (scientific name without author strings and year)
+ * string of <i>this</i> non viral taxon name.
+ *
+ * @return the boolean value of the protectedNameCache flag
+ * @see #getNameCache()
+ */
+ public boolean isProtectedNameCache() {
+ return protectedNameCache;
+ }
+
+ /**
+ * @see #isProtectedNameCache()
+ */
+ public void setProtectedNameCache(boolean protectedNameCache) {
+ this.protectedNameCache = protectedNameCache;
+ }
+
+
+ /**
+ * Generates and returns a concatenated and formated authorteams string
+ * including basionym and combination authors of <i>this</i> non viral taxon name
+ * according to the strategy defined in
+ * {@link eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName) INonViralNameCacheStrategy}.
+ *
+ * @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
+ * @see eu.etaxonomy.cdm.strategy.cache.name.INonViralNameCacheStrategy#getAuthorshipCache(NonViralName)
+ */
+ public String generateAuthorship(){
+ if (cacheStrategy == null){
+ logger.warn("No CacheStrategy defined for nonViralName: " + this.getUuid());
+ return null;
+ }else{
+ return ((INonViralNameCacheStrategy<T>)cacheStrategy).getAuthorshipCache((T)this);
+ }
+ }
+
+ /**
+ * Returns the concatenated and formated authorteams string including
+ * basionym and combination authors of <i>this</i> non viral taxon name.
+ * If the protectedAuthorshipCache flag is set this method returns the
+ * string stored in the the authorshipCache attribute, otherwise it
+ * generates the complete authorship string, returns it and stores it in
+ * the authorshipCache attribute.
+ *
+ * @return the string with the concatenated and formated authorteams for <i>this</i> non viral taxon name
+ * @see #generateAuthorship()
+ */
+ @Transient
+ public String getAuthorshipCache() {
+ if (protectedAuthorshipCache){
+ return this.authorshipCache;
+ }
+ if (this.authorshipCache == null ){
+ this.authorshipCache = generateAuthorship();
+ }else{
+ //TODO get is Dirty of authors, make better if possible
+ this.setAuthorshipCache(generateAuthorship(), protectedAuthorshipCache); //throw change event to inform higher caches
+
+ }
+ return authorshipCache;
+ }
+
+
+ /**
+ * Updates the authorship cache if any changes appeared in the authors nomenclatural caches.
+ * Deletes the titleCache and the fullTitleCache if not protected and if any change has happened
+ * @return
+ */
+ private void updateAuthorshipCache() {
+ //updates the authorship cache if necessary and via the listener updates all higher caches
+ if (protectedAuthorshipCache == false){
+ String oldCache = this.authorshipCache;
+ String newCache = this.getAuthorshipCache();
+ if ( (oldCache == null && newCache != null) || CdmUtils.nullSafeEqual(oldCache,newCache)){
+ this.setAuthorshipCache(this.getAuthorshipCache(), false);
+ }
+ }
+ }
+
+ /**
+ * Assigns an authorshipCache string to <i>this</i> non viral taxon name. Sets the isProtectedAuthorshipCache
+ * flag to <code>true</code>.
+ *
+ * @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
+ * @see #getAuthorshipCache()
+ */
+ public void setAuthorshipCache(String authorshipCache) {
+ setAuthorshipCache(authorshipCache, true);
+ }
+
+ @Override
+ @Transient
+ public String getFullTitleCache(){
+ updateAuthorshipCache();
+ return super.getFullTitleCache();
+ }
+
+ @Override
+ public String getTitleCache(){
+ if(!protectedTitleCache) {
+ updateAuthorshipCache();
+ }
+
+ return super.getTitleCache();
+ }
+
+
+ /**
+ * Assigns an authorshipCache string to <i>this</i> non viral taxon name.
+ *
+ * @param authorshipCache the string which identifies the complete authorship of <i>this</i> non viral taxon name
+ * @param protectedAuthorshipCache if true the isProtectedAuthorshipCache flag is set to <code>true</code>, otherwise
+ * the flag is set to <code>false</code>.
+ * @see #getAuthorshipCache()
+ */
+ public void setAuthorshipCache(String authorshipCache, boolean protectedAuthorshipCache) {
+ this.authorshipCache = authorshipCache;
+ this.setProtectedAuthorshipCache(protectedAuthorshipCache);
+ }
+
+ @Override
+ public void setTitleCache(String titleCache, boolean protectCache){
+ super.setTitleCache(titleCache, protectCache);
+ }
+
+ /**
+ * Returns the boolean value "false" since the components of <i>this</i> taxon name
+ * cannot follow the rules of a corresponding {@link NomenclaturalCode nomenclatural code}
+ * which is not defined for this class. The nomenclature code depends on
+ * the concrete name subclass ({@link BacterialName BacterialName},
+ * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} or
+ * {@link ZoologicalName ZoologicalName} to which <i>this</i> non viral taxon name belongs.
+ * This method overrides the isCodeCompliant method from the abstract
+ * {@link TaxonNameBase#isCodeCompliant() TaxonNameBase} class.
+ *
+ * @return false
+ * @see TaxonNameBase#isCodeCompliant()
+ */
+ @Override
+ @Transient
+ public boolean isCodeCompliant() {
+ //FIXME
+ logger.warn("is CodeCompliant not yet implemented");
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#getNomeclaturalCode()
+ */
+ /**
+ * Returns null as {@link NomenclaturalCode nomenclatural code} that governs
+ * the construction of <i>this</i> non viral taxon name since there is no specific
+ * nomenclatural code defined. The real implementention takes place in the
+ * subclasses {@link BacterialName BacterialName},
+ * {@link BotanicalName BotanicalName}, {@link CultivarPlantName CultivarPlantName} and
+ * {@link ZoologicalName ZoologicalName}.
+ * This method overrides the getNomeclaturalCode method from {@link TaxonNameBase TaxonNameBase}.
+ *
+ * @return null
+ * @see #isCodeCompliant()
+ * @see TaxonNameBase#getHasProblem()
+ */
+ @Override
+ @Transient
+ public NomenclaturalCode getNomenclaturalCode() {
+ logger.warn("Non Viral Name has no specific Code defined. Use subclasses");
+ return null;
+ }
+
+ /**
+ * Returns the boolean value of the flag intended to protect (true)
+ * or not (false) the {@link #getAuthorshipCache() authorshipCache} (complete authorship string)
+ * of <i>this</i> non viral taxon name.
+ *
+ * @return the boolean value of the protectedAuthorshipCache flag
+ * @see #getAuthorshipCache()
+ */
+ public boolean isProtectedAuthorshipCache() {
+ return protectedAuthorshipCache;
+ }
+
+ /**
+ * @see #isProtectedAuthorshipCache()
+ * @see #getAuthorshipCache()
+ */
+ public void setProtectedAuthorshipCache(boolean protectedAuthorshipCache) {
+ this.protectedAuthorshipCache = protectedAuthorshipCache;
+ }
+
+
+ /**
+ * Returns the boolean value of the flag indicating whether the name of <i>this</i>
+ * botanical taxon name is a hybrid formula (true) or not (false). A hybrid
+ * named by a hybrid formula (composed with its parent names by placing the
+ * multiplication sign between them) does not have an own published name
+ * and therefore has neither an {@link NonViralName#getAuthorshipCache() autorship}
+ * nor other name components. If this flag is set no other hybrid flags may
+ * be set.
+ *
+ * @return the boolean value of the isHybridFormula flag
+ * @see #isMonomHybrid()
+ * @see #isBinomHybrid()
+ * @see #isTrinomHybrid()
+ */
+ public boolean isHybridFormula(){
+ return this.hybridFormula;
+ }
+
+ /**
+ * @see #isHybridFormula()
+ */
+ public void setHybridFormula(boolean hybridFormula){
+ this.hybridFormula = hybridFormula;
+ }
+
+ /**
+ * Returns the boolean value of the flag indicating whether <i>this</i> botanical
+ * taxon name is the name of an intergeneric hybrid (true) or not (false).
+ * In this case the multiplication sign is placed before the scientific
+ * name. If this flag is set no other hybrid flags may be set.
+ *
+ * @return the boolean value of the isMonomHybrid flag
+ * @see #isHybridFormula()
+ * @see #isBinomHybrid()
+ * @see #isTrinomHybrid()
+ */
+ public boolean isMonomHybrid(){
+ return this.monomHybrid;
+ }
+
+ /**
+ * @see #isMonomHybrid()
+ * @see #isBinomHybrid()
+ * @see #isTrinomHybrid()
+ */
+ public void setMonomHybrid(boolean monomHybrid){
+ this.monomHybrid = monomHybrid;
+ }
+
+ /**
+ * Returns the boolean value of the flag indicating whether <i>this</i> botanical
+ * taxon name is the name of an interspecific hybrid (true) or not (false).
+ * In this case the multiplication sign is placed before the species
+ * epithet. If this flag is set no other hybrid flags may be set.
+ *
+ * @return the boolean value of the isBinomHybrid flag
+ * @see #isHybridFormula()
+ * @see #isMonomHybrid()
+ * @see #isTrinomHybrid()
+ */
+ public boolean isBinomHybrid(){
+ return this.binomHybrid;
+ }
+
+ /**
+ * @see #isBinomHybrid()
+ * @see #isMonomHybrid()
+ * @see #isTrinomHybrid()
+ */
+ public void setBinomHybrid(boolean binomHybrid){
+ this.binomHybrid = binomHybrid;
+ }
+
+ /**
+ * Returns the boolean value of the flag indicating whether <i>this</i> botanical
+ * taxon name is the name of an infraspecific hybrid (true) or not (false).
+ * In this case the term "notho-" (optionally abbreviated "n-") is used as
+ * a prefix to the term denoting the infraspecific rank of <i>this</i> botanical
+ * taxon name. If this flag is set no other hybrid flags may be set.
+ *
+ * @return the boolean value of the isTrinomHybrid flag
+ * @see #isHybridFormula()
+ * @see #isMonomHybrid()
+ * @see #isBinomHybrid()
+ */
+ public boolean isTrinomHybrid(){
+ return this.trinomHybrid;
+ }
+
+ /**
+ * @see #isTrinomHybrid()
+ * @see #isBinomHybrid()
+ * @see #isMonomHybrid()
+ */
+ public void setTrinomHybrid(boolean trinomHybrid){
+ this.trinomHybrid = trinomHybrid;
+ }
+
+
+ /**
+ * Returns the set of all {@link HybridRelationship hybrid relationships}
+ * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
+ *
+ * @see #getHybridRelationships()
+ * @see #getChildRelationships()
+ * @see HybridRelationshipType
+ */
+ public Set<HybridRelationship> getHybridParentRelations() {
+ if(hybridParentRelations == null) {
+ this.hybridParentRelations = new HashSet<HybridRelationship>();
+ }
+ return hybridParentRelations;
+ }
+
+ private void setHybridParentRelations(Set<HybridRelationship> hybridParentRelations) {
+ this.hybridParentRelations = hybridParentRelations;
+ }
+
+
+ /**
+ * Returns the set of all {@link HybridRelationship hybrid relationships}
+ * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedTo() child}.
+ *
+ * @see #getHybridRelationships()
+ * @see #getParentRelationships()
+ * @see HybridRelationshipType
+ */
+ public Set<HybridRelationship> getHybridChildRelations() {
+ if(hybridChildRelations == null) {
+ this.hybridChildRelations = new HashSet<HybridRelationship>();
+ }
+ return hybridChildRelations;
+ }
+
+ private void setHybridChildRelations(Set<HybridRelationship> hybridChildRelations) {
+ this.hybridChildRelations = hybridChildRelations;
+ }
+
+ /**
+ * Returns the set of all {@link HybridRelationship hybrid relationships}
+ * in which <i>this</i> taxon name is involved as a {@link common.RelationshipBase#getRelatedFrom() parent}.
+ * @see #getHybridParentRelations()
+ * @see #getHybridRelationships()
+ * @see #getChildRelationships()
+ * @see HybridRelationshipType
+ * @deprecated use {@link #getHybridParentRelations()} instead. Will be removed in higher versions.
+ */
+ @Deprecated
+ @Transient
+ public Set<HybridRelationship> getParentRelationships() {
+ return getHybridParentRelations();
+ }
+
+ /**
+ * Returns the hybrid child relationships ordered by relationship type, or if equal
+ * by title cache of the related names.
+ * @see #getHybridParentRelations()
+ */
+ @Transient
+ public List<HybridRelationship> getOrderedChildRelationships(){
+ List<HybridRelationship> result = new ArrayList<HybridRelationship>();
+ result.addAll(this.hybridChildRelations);
+ Collections.sort(result);
+ Collections.reverse(result);
+ return result;
+
+ }
+
+
+ /**
+ * @see #getHybridChildRelations()
+ * @deprecated use {@link #getHybridChildRelations()} instead. Will be removed in higher versions.
+ */
+ @Transient
+ @Deprecated
+ public Set<HybridRelationship> getChildRelationships() {
+ return this.getHybridChildRelations();
+ }
+
+ /**
+ * Adds the given {@link HybridRelationship hybrid relationship} to the set
+ * of {@link #getHybridRelationships() hybrid relationships} of both non-viral names
+ * involved in this hybrid relationship. One of both non-viral names
+ * must be <i>this</i> non-viral name otherwise no addition will be carried
+ * out. The {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedTo() child
+ * non viral taxon name} must be a hybrid, which means that one of its four hybrid flags must be set.
+ *
+ * @param relationship the hybrid relationship to be added
+ * @see #isHybridFormula()
+ * @see #isMonomHybrid()
+ * @see #isBinomHybrid()
+ * @see #isTrinomHybrid()
+ * @see #getHybridRelationships()
+ * @see #getParentRelationships()
+ * @see #getChildRelationships()
+ * @see #addRelationship(RelationshipBase)
+ * @throws IllegalArgumentException
+ */
+ protected void addHybridRelationship(HybridRelationship rel) {
+ if (rel!=null && rel.getHybridName().equals(this)){
+ this.hybridChildRelations.add(rel);
+ }else if(rel!=null && rel.getParentName().equals(this)){
+ this.hybridParentRelations.add(rel);
+ }else{
+ throw new IllegalArgumentException("Hybrid relationship is either null or the relationship does not reference this name");
+ }
+ }
+
+
+
+ /**
+ * Does the same as the addHybridRelationship method if the given
+ * {@link common.RelationshipBase relation} is also a {@link HybridRelationship hybrid relationship}.
+ * Otherwise this method does the same as the overwritten {@link TaxonNameBase#addRelationship(RelationshipBase) addRelationship}
+ * method from TaxonNameBase.
+ *
+ * @param relation the relationship to be added to some of <i>this</i> taxon name's relationships sets
+ * @see #addHybridRelationship(HybridRelationship)
+ * @see TaxonNameBase#addRelationship(RelationshipBase)
+ * @see TaxonNameBase#addNameRelationship(NameRelationship)
+ * @deprecated to be used by RelationshipBase only
+ */
+ @Override
+ @Deprecated //to be used by RelationshipBase only
+ public void addRelationship(RelationshipBase relation) {
+ if (relation instanceof HybridRelationship){
+ addHybridRelationship((HybridRelationship)relation);
+ }else {
+ super.addRelationship(relation);
+ }
+ }
+
+ /**
+ * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
+ * to <i>this</i> botanical name. A HybridRelationship may be of type
+ * "is first/second parent" or "is male/female parent". By invoking this
+ * method <i>this</i> botanical name becomes a hybrid child of the parent
+ * botanical name.
+ *
+ * @param parentName the botanical name of the parent for this new hybrid name relationship
+ * @param type the type of this new name relationship
+ * @param ruleConsidered the string which specifies the rule on which this name relationship is based
+ * @return
+ * @see #addHybridChild(BotanicalName, HybridRelationshipType,String )
+ * @see #getRelationsToThisName()
+ * @see #getNameRelations()
+ * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
+ * @see #addNameRelationship(NameRelationship)
+ */
+ public HybridRelationship addHybridParent(NonViralName parentName, HybridRelationshipType type, String ruleConsidered){
+ return new HybridRelationship(this, parentName, type, ruleConsidered);
+ }
+
+ /**
+ * Creates a new {@link HybridRelationship#HybridRelationship(BotanicalName, BotanicalName, HybridRelationshipType, String) hybrid relationship}
+ * to <i>this</i> botanical name. A HybridRelationship may be of type
+ * "is first/second parent" or "is male/female parent". By invoking this
+ * method <i>this</i> botanical name becomes a parent of the hybrid child
+ * botanical name.
+ *
+ * @param childName the botanical name of the child for this new hybrid name relationship
+ * @param type the type of this new name relationship
+ * @param ruleConsidered the string which specifies the rule on which this name relationship is based
+ * @return
+ * @see #addHybridParent(BotanicalName, HybridRelationshipType,String )
+ * @see #getRelationsToThisName()
+ * @see #getNameRelations()
+ * @see #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
+ * @see #addNameRelationship(NameRelationship)
+ */
+ public HybridRelationship addHybridChild(NonViralName childName, HybridRelationshipType type, String ruleConsidered){
+ return new HybridRelationship(childName, this, type, ruleConsidered);
+ }
+
+
+ /**
+ * Removes one {@link HybridRelationship hybrid relationship} from the set of
+ * {@link #getHybridRelationships() hybrid relationships} in which <i>this</i> botanical taxon name
+ * is involved. The hybrid relationship will also be removed from the set
+ * belonging to the second botanical taxon name involved.
+ *
+ * @param relationship the hybrid relationship which should be deleted from the corresponding sets
+ * @see #getHybridRelationships()
+ */
+ public void removeHybridRelationship(HybridRelationship hybridRelation) {
+ if (hybridRelation == null) {
+ return;
+ }
+
+ NonViralName parent = hybridRelation.getParentName();
+ NonViralName child = hybridRelation.getHybridName();
+
+ hybridRelation.setHybridName(null);
+ hybridRelation.setParentName(null);
+
+ if (parent != null) {
+ parent.removeHybridRelationship(hybridRelation);
+ }
+
+ if (child != null) {
+ child.removeHybridRelationship(hybridRelation);
+ }
+
+ this.hybridChildRelations.remove(hybridRelation);
+ this.hybridParentRelations.remove(hybridRelation);
+ }
+
+
+ public void removeHybridChild(NonViralName child) {
+ Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
+ hybridRelationships.addAll(this.getChildRelationships());
+ hybridRelationships.addAll(this.getParentRelationships());
+ for(HybridRelationship hybridRelationship : hybridRelationships) {
+ // remove name relationship from this side
+ if (hybridRelationship.getParentName().equals(this) && hybridRelationship.getHybridName().equals(child)) {
+ this.removeHybridRelationship(hybridRelationship);
+ }
+ }
+ }
+
+ public void removeHybridParent(NonViralName parent) {
+ Set<HybridRelationship> hybridRelationships = new HashSet<HybridRelationship>();
+ hybridRelationships.addAll(this.getChildRelationships());
+ hybridRelationships.addAll(this.getParentRelationships());
+ for(HybridRelationship hybridRelationship : hybridRelationships) {
+ // remove name relationship from this side
+ if (hybridRelationship.getParentName().equals(parent) && hybridRelationship.getHybridName().equals(this)) {
+ this.removeHybridRelationship(hybridRelationship);
+ }
+ }
+ }
+
+ /**
+ * Needs to be implemented by those classes that handle autonyms (e.g. botanical names).
+ **/
+ @Transient
+ public boolean isAutonym(){
+ return false;
+ }
+
+
+// /**
+// * Returns the boolean value indicating whether <i>this</i> names rank is Rank "unranked"
+// * (uuid = 'a965befb-70a9-4747-a18f-624456c65223') but most likely it is an infrageneric rank
+// * due to existing atomized data for the genus epithet and the infrageneric epithet but missing
+// * specific epithet.
+// * Returns false if <i>this</i> names rank is null.
+// *
+// * @see #isSupraGeneric()
+// * @see #isGenus()
+// * @see #isSpeciesAggregate()
+// * @see #isSpecies()
+// * @see #isInfraSpecific()
+// */
+// @Transient
+// public boolean isInfragenericUnranked() {
+// Rank rank = this.getRank();
+// if (rank == null || ! rank.equals(Rank.UNRANKED())){
+// return false;
+// }
+// if (StringUtils.isBlank(this.getSpecificEpithet()) && StringUtils.isBlank(this.getInfraSpecificEpithet()) ){
+// return true;
+// }else{
+// return false;
+// }
+// }
+
+
+ /**
+ * Tests if the given name has any authors.
+ * @return false if no author ((ex)combination or (ex)basionym) exists, true otherwise
+ */
+ public boolean hasAuthors() {
+ return (this.getCombinationAuthorTeam() != null ||
+ this.getExCombinationAuthorTeam() != null ||
+ this.getBasionymAuthorTeam() != null ||
+ this.getExBasionymAuthorTeam() != null);
+ }
+
+ /**
+ * Shortcut. Returns the combination authors title cache. Returns null if no combination author exists.
+ * @return
+ */
+ public String computeCombinationAuthorNomenclaturalTitle() {
+ return computeNomenclaturalTitle(this.getCombinationAuthorTeam());
+ }
+
+ /**
+ * Shortcut. Returns the basionym authors title cache. Returns null if no basionym author exists.
+ * @return
+ */
+ public String computeBasionymAuthorNomenclaturalTitle() {
+ return computeNomenclaturalTitle(this.getBasionymAuthorTeam());
+ }
+
+
+ /**
+ * Shortcut. Returns the ex-combination authors title cache. Returns null if no ex-combination author exists.
+ * @return
+ */
+ public String computeExCombinationAuthorNomenclaturalTitle() {
+ return computeNomenclaturalTitle(this.getExCombinationAuthorTeam());
+ }
+
+ /**
+ * Shortcut. Returns the ex-basionym authors title cache. Returns null if no exbasionym author exists.
+ * @return
+ */
+ public String computeExBasionymAuthorNomenclaturalTitle() {
+ return computeNomenclaturalTitle(this.getExBasionymAuthorTeam());
+ }
+
+ private String computeNomenclaturalTitle(INomenclaturalAuthor author){
+ if (author == null){
+ return null;
+ }else{
+ return author.getNomenclaturalTitle();
+ }
+ }
+
+//*********************** CLONE ********************************************************/
+
+ /**
+ * Clones <i>this</i> non-viral name. This is a shortcut that enables to create
+ * a new instance that differs only slightly from <i>this</i> non-viral name by
+ * modifying only some of the attributes.
+ *
+ * @see eu.etaxonomy.cdm.model.name.TaxonNameBase#clone()
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() {
+ NonViralName result = (NonViralName)super.clone();
+
+ //HybridChildRelations
+ result.hybridChildRelations = new HashSet<HybridRelationship>();
+ for (HybridRelationship hybridRelationship : getHybridChildRelations()){
+ HybridRelationship newChildRelationship = (HybridRelationship)hybridRelationship.clone();
+ newChildRelationship.setRelatedTo(result);
+ result.hybridChildRelations.add(newChildRelationship);
+ }
+
+ //HybridParentRelations
+ result.hybridParentRelations = new HashSet<HybridRelationship>();
+ for (HybridRelationship hybridRelationship : getHybridParentRelations()){
+ HybridRelationship newParentRelationship = (HybridRelationship)hybridRelationship.clone();
+ newParentRelationship.setRelatedFrom(result);
+ result.hybridParentRelations.add(newParentRelationship);
+ }
+
+ //empty caches
+ if (! protectedNameCache){
+ result.nameCache = null;
+ }
+
+ //empty caches
+ if (! protectedAuthorshipCache){
+ result.authorshipCache = null;
+ }