ref #9359 upgrade cdmlib to log4j 2
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / term / OrderedTermBase.java
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.model.term;
10
11 import java.util.UUID;
12
13 import javax.persistence.Entity;
14 import javax.persistence.Transient;
15 import javax.xml.bind.annotation.XmlAccessType;
16 import javax.xml.bind.annotation.XmlAccessorType;
17 import javax.xml.bind.annotation.XmlElement;
18 import javax.xml.bind.annotation.XmlSeeAlso;
19 import javax.xml.bind.annotation.XmlType;
20
21 import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
22 import org.hibernate.envers.Audited;
23
24 import eu.etaxonomy.cdm.common.CdmUtils;
25 import eu.etaxonomy.cdm.model.common.CdmBase;
26 import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
27 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
28 import eu.etaxonomy.cdm.model.description.State;
29 import eu.etaxonomy.cdm.model.location.NamedArea;
30 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
31 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
32 import eu.etaxonomy.cdm.model.name.Rank;
33
34 /**
35 * @author m.doering
36 * @since 08-Nov-2007 13:06:23
37 */
38 @XmlAccessorType(XmlAccessType.FIELD)
39 @XmlType(name = "OrderedTermBase", propOrder = {
40 "orderIndex"
41 })
42 @XmlSeeAlso({
43 RelationshipTermBase.class,
44 PresenceAbsenceTerm.class,
45 State.class,
46 NamedArea.class,
47 NamedAreaLevel.class,
48 NomenclaturalStatusType.class,
49 Rank.class
50 })
51 @Entity
52 @Audited
53 public abstract class OrderedTermBase<T extends OrderedTermBase<T>>
54 extends DefinedTermBase<T> {
55
56 private static final long serialVersionUID = 8000797926720467399L;
57 @SuppressWarnings("unused")
58 private static final Logger logger = LogManager.getLogger(OrderedTermBase.class);
59
60 //Order index, value < 1 means that this Term is not in order yet
61 @XmlElement(name = "OrderIndex")
62 protected int orderIndex;
63
64 /**
65 * Higher ordered terms have a lower order index,
66 * lower ordered terms have a higher order index:
67 * <p>
68 * <b>a.oderIndex &lt; b.oderIndex : a &gt; b</b>
69 * @return the order index of a term
70 */
71 public int getOrderIndex() {
72 return orderIndex;
73 }
74
75 // *********************** CONSTRUCTOR *************************/
76
77 //for JAXB only, TODO needed?
78 @Deprecated
79 protected OrderedTermBase(){}
80
81 protected OrderedTermBase(TermType type) {
82 super(type);
83 }
84 public OrderedTermBase(TermType type, String description, String label, String labelAbbrev) {
85 super(type, description, label, labelAbbrev);
86 }
87
88 // **************************** METHODS ******************************/
89
90 /**
91 * Compares this OrderedTermBase with the specified OrderedTermBase for
92 * order. Returns a -1, 0, or +1 if the orderIndex of this object is greater
93 * than, equal to, or less than the specified object. In case the parameter
94 * is <code>null</code> the
95 * <p>
96 * <b>Note:</b> The compare logic of this method might appear to be <b>inverse</b>
97 * to the one mentioned in
98 * {@link java.lang.Comparable#compareTo(java.lang.Object)}. This is, because the logic here
99 * is that the lower the orderIndex the higher the term. E.g. the very high {@link Rank}
100 * Kingdom may have an orderIndex close to 1.
101 *
102 * @param orderedTerm
103 * the OrderedTermBase to be compared
104 * @throws NullPointerException
105 * if the specified object is null
106 */
107 @Override
108 public int compareTo(T orderedTerm) {
109 return performCompareTo(orderedTerm, false);
110 }
111
112 /**
113 * Compares this {@link OrderedTermBase ordered term} with the given {@link OrderedTermBase thatTerm} for
114 * order. Returns a -1, 0, or +1 if the orderId of this object is greater
115 * than, equal to, or less than the specified object.
116 * <p>
117 * <b>Note:</b> The compare logic of this method is the <b>inverse logic</b>
118 * of the the one implemented in
119 * {@link java.lang.Comparable#compareTo(java.lang.Object)}
120 *
121 * @param orderedTerm
122 * the OrderedTermBase to be compared
123 * @param skipVocabularyCheck
124 * whether to skip checking if both terms to compare are in the
125 * same vocabulary
126 * @throws NullPointerException
127 * if the specified object is null
128 */
129 protected int performCompareTo(T thatTerm , boolean skipVocabularyCheck ) {
130
131 T thatTermLocal = CdmBase.deproxy(thatTerm);
132 if(!skipVocabularyCheck){
133 if (this.vocabulary == null || thatTermLocal.vocabulary == null){
134 throw new IllegalStateException("An ordered term (" + this.toString() + " or " + thatTermLocal.toString() + ") of class " + this.getClass() + " or " + thatTermLocal.getClass() + " does not belong to a vocabulary and therefore can not be compared");
135 }
136 if (! this.getVocabulary().getUuid().equals(thatTermLocal.vocabulary.getUuid())){
137 throw new IllegalStateException("2 terms do not belong to the same vocabulary and therefore can not be compared: " + this.getTitleCache() + " and " + thatTermLocal.getTitleCache());
138 }
139 }
140
141 int vocCompare = compareVocabularies(thatTermLocal);
142 if (vocCompare != 0){
143 return vocCompare;
144 }
145
146 int orderThat;
147 int orderThis;
148 try {
149 orderThat = thatTermLocal.orderIndex;
150 orderThis = orderIndex;
151 } catch (RuntimeException e) {
152 throw e;
153 }
154 if (orderThis > orderThat){
155 return -1;
156 }else if (orderThis < orderThat){
157 return 1;
158 }else {
159 if (skipVocabularyCheck){
160 String errorStr = "The term %s (ID: %s) is not attached to any vocabulary. This should not happen. "
161 + "Please add the term to an vocabulary";
162 if (this.vocabulary == null){
163 throw new IllegalStateException(String.format(errorStr, this.getLabel(), String.valueOf(this.getId())));
164 }else if (thatTermLocal.vocabulary == null){
165 throw new IllegalStateException(String.format(errorStr, thatTermLocal.getLabel(), String.valueOf(thatTermLocal.getId())));
166 }
167 }
168 return 0;
169 }
170 }
171
172 protected int compareVocabularies(T thatTerm) {
173 //if vocabularies are not equal order by voc.uuid to get a defined behavior
174 //ordering terms from 2 different vocabularies is generally not recommended
175 UUID thisVocUuid = this.vocabulary == null? null:this.vocabulary.getUuid();
176 UUID thatVocUuid = thatTerm.getVocabulary() == null? null:thatTerm.getVocabulary().getUuid();
177 int vocCompare = CdmUtils.nullSafeCompareTo(thisVocUuid, thatVocUuid);
178 return vocCompare;
179 }
180
181 /**
182 * If this term is lower than the parameter term, true is returned, else false.
183 * If the parameter term is null, an Exception is thrown.
184 * @param orderedTerm
185 * @return boolean result of the comparison
186 */
187 public boolean isLower(T orderedTerm){
188 return (this.compareTo(orderedTerm) < 0 );
189 }
190
191 /**
192 * If this term is higher than the parameter term, true is returned, else false.
193 * If the parameter term is null, an Exception is thrown.
194 * @param orderedTerm
195 * @return boolean result of the comparison
196 */
197 public boolean isHigher(T orderedTerm){
198 return (this.compareTo(orderedTerm) > 0 );
199 }
200
201 /**
202 * @deprecated To be used only by OrderedTermVocabulary
203 **/
204 @Deprecated
205 protected boolean decreaseIndex(OrderedTermVocabulary vocabulary){
206 if (vocabulary.indexChangeAllowed(this) == true){
207 orderIndex--;
208 return true;
209 }else{
210 return false;
211 }
212 }
213
214 /**
215 * @deprecated To be used only by OrderedTermVocabulary
216 **/
217 @Deprecated
218 protected boolean incrementIndex(OrderedTermVocabulary vocabulary){
219 if (vocabulary.indexChangeAllowed(this) == true){
220 orderIndex++;
221 return true;
222 }else{
223 return false;
224 }
225 }
226
227
228 @SuppressWarnings("unchecked")
229 @Transient
230 public T getNextHigherTerm(){ //#3327
231 if (getVocabulary() == null){
232 return null;
233 }else{
234 OrderedTermBase<T> result = CdmBase.deproxy(getVocabulary(), OrderedTermVocabulary.class).getNextHigherTerm(this);
235 return (T)result;
236 }
237 }
238
239 @SuppressWarnings("unchecked")
240 @Transient
241 public T getNextLowerTerm(){ //#3327
242 if (getVocabulary() == null){
243 return null;
244 }else{
245 OrderedTermBase<T> result = CdmBase.deproxy(getVocabulary(), OrderedTermVocabulary.class).getNextLowerTerm(this);
246 return (T)result;
247 }
248 }
249
250 //*********************** CLONE ********************************************************/
251
252 /**
253 * Clones <i>this</i> OrderedTermBase. This is a shortcut that enables to create
254 * a new instance that differs only slightly from <i>this</i> OrderedTermBase.
255 *
256 * @see eu.etaxonomy.cdm.model.term.DefinedTermBase#clone()
257 * @see java.lang.Object#clone()
258 */
259 @Override
260 public OrderedTermBase<T> clone() {
261 OrderedTermBase<T> result = (OrderedTermBase<T>) super.clone();
262 //no changes to orderIndex
263 return result;
264 }
265 }