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