Merge branch 'release/5.43.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / description / CategoricalData.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.description;
10
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.List;
14
15 import javax.persistence.Column;
16 import javax.persistence.Entity;
17 import javax.persistence.FetchType;
18 import javax.persistence.OneToMany;
19 import javax.persistence.Transient;
20 import javax.validation.constraints.NotEmpty;
21 import javax.xml.bind.annotation.XmlAccessType;
22 import javax.xml.bind.annotation.XmlAccessorType;
23 import javax.xml.bind.annotation.XmlAttribute;
24 import javax.xml.bind.annotation.XmlElement;
25 import javax.xml.bind.annotation.XmlElementWrapper;
26 import javax.xml.bind.annotation.XmlRootElement;
27 import javax.xml.bind.annotation.XmlTransient;
28 import javax.xml.bind.annotation.XmlType;
29
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
32 import org.hibernate.annotations.Cascade;
33 import org.hibernate.annotations.CascadeType;
34 import org.hibernate.annotations.Type;
35 import org.hibernate.envers.Audited;
36 import org.hibernate.search.annotations.Indexed;
37 import org.hibernate.search.annotations.IndexedEmbedded;
38
39 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
40 import eu.etaxonomy.cdm.validation.Level2;
41
42 /**
43 * This class represents information pieces expressed in categorical type of
44 * data (in opposition to {@link QuantitativeData quantitative data} on one side and to literal data on
45 * the other side). Only {@link TaxonDescription taxon descriptions} and
46 * {@link SpecimenDescription specimen descriptions} may contain categorical data.<BR>
47 * The "color of petals" {@link Feature feature} for instance can be described with
48 * {@link State state terms} such as "blue" or "white". If the color of petals of a
49 * particular tree is described as "mostly blue" and "exceptionally white" two
50 * {@link StateData state data} instances must be assigned to an instance of the
51 * present class: the first one with the state "blue" and the {@link Modifier modifier}
52 * "mostly" and the second one with the state "white" and the modifier "exceptionally".
53 * Whenever more than one state data belongs to a categorical data they should be
54 * interpreted as being related by the inclusive disjunction "or".
55 * <P>
56 * This class corresponds partially to CodedDescriptionType according to
57 * the SDD schema.
58 *
59 * @author m.doering
60 * @since 08-Nov-2007 13:06:15
61 */
62 @XmlAccessorType(XmlAccessType.FIELD)
63 @XmlType(name = "CategoricalData", propOrder = {
64 "orderRelevant",
65 "stateData",
66 "noDataStatus"
67 })
68 @XmlRootElement(name = "CategoricalData")
69 @Entity
70 @Audited
71 @Indexed(index = "eu.etaxonomy.cdm.model.description.DescriptionElementBase")
72 public class CategoricalData extends DescriptionElementBase {
73
74 private static final long serialVersionUID = -6298361966947668998L;
75 @SuppressWarnings("unused")
76 private static final Logger logger = LogManager.getLogger();
77
78 //whether the sequence of ordered states is important
79 @XmlElement(name = "OrderRelevant")
80 private boolean orderRelevant;
81
82 @XmlElementWrapper(name = "States")
83 @XmlElement(name = "State")
84 @OneToMany(fetch = FetchType.LAZY, mappedBy="categoricalData", orphanRemoval=true)
85 @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
86 @IndexedEmbedded(depth = 3)
87 @NotEmpty(groups = Level2.class)
88 private List<StateData> stateData = new ArrayList<>();
89
90 @XmlAttribute(name ="NoDataStatus")
91 @Column(name="noDataStatus", length=10)
92 @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
93 parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.description.NoDescriptiveDataStatus")}
94 )
95 //see also QuantitativeData.noDataStatus
96 private NoDescriptiveDataStatus noDataStatus;
97
98 //****************************** FACTORY METHOD *******************************/
99
100 /**
101 * Creates a new empty categorical data instance.
102 */
103 public static CategoricalData NewInstance(){
104 return new CategoricalData();
105 }
106
107 public static CategoricalData NewInstance(Feature feature) {
108 return new CategoricalData( new ArrayList<>() , feature);
109 }
110
111 /**
112 * Creates a new empty categorical data instance.
113 */
114 public static CategoricalData NewInstance(DefinedTermBase<?> state, Feature feature){
115 return new CategoricalData( Arrays.asList( new DefinedTermBase<?>[]{state}) , feature);
116 }
117
118 //******************* CONSTRUCTOR *********************************************/
119
120 /**
121 * Class constructor: creates a new empty categorical data instance.
122 */
123 protected CategoricalData() {
124 super(null);
125 }
126
127 /**
128 * Class constructor: creates a new empty categorical data instance.
129 */
130 protected CategoricalData(List<DefinedTermBase<?>> states, Feature feature) {
131 super(feature);
132 for (DefinedTermBase<?> state : states){
133 addStateData(state);
134 }
135 }
136
137 // ****************** GETTER / SETTER *********************************************/
138
139 /**
140 * Returns the (ordered) list of {@link State states} describing the {@link Feature feature}
141 * corresponding to <i>this</i> categorical data.
142 */
143 public List<StateData> getStateData(){
144 return this.stateData;
145 }
146
147 @Deprecated
148 protected void setStateData(List<StateData> stateData){
149 this.stateData = stateData;
150 }
151
152 /**
153 * Adds a {@link State state} to the list of {@link #getStateData() states}
154 * describing the {@link Feature feature} corresponding to <i>this</i> categorical data.
155 *
156 * @param state the state to be added to <i>this</i> categorical data
157 * @see #getStateData()
158 */
159 @SuppressWarnings("deprecation")
160 public void addStateData(StateData stateData){
161 this.stateData.add(stateData);
162 stateData.setCategoricalData(this);
163 }
164
165 /**
166 * Convenience method which creates a state data from a given state with no modifiers
167 * and adds it to the list of state data
168 * @see #addStateData(StateData)
169 * @param state
170 */
171 public StateData addStateData(DefinedTermBase<?> state){
172 StateData stateData = StateData.NewInstance(state);
173 addStateData(stateData);
174 return stateData;
175 }
176
177 /**
178 * Removes one element from the set of {@link #getStateData() states}
179 * describing the {@link Feature feature} corresponding to <i>this</i> categorical data.
180 *
181 * @param state the state which should be removed
182 * @see #getStateData()
183 * @see #addStateData(State)
184 */
185 @SuppressWarnings("deprecation")
186 public void removeStateData(StateData stateData){
187 this.stateData.remove(stateData);
188 stateData.setCategoricalData(null);
189 }
190
191 //rename to isStateSequenceIntentional ??
192 /**
193 * Returns the boolean value of the flag indicating whether the sequence of
194 * {@link StateData state data} belonging to <i>this</i> categorical data is intentional
195 * (true) and therefore relevant for interpretation or analysis or not (false).
196 * The use of this flag depends mostly on the {@link Feature feature} of <i>this</i> categorical data.
197 *
198 * @return the boolean value of the orderRelevant flag
199 */
200 public boolean getOrderRelevant(){
201 return this.orderRelevant;
202 }
203 /**
204 * @see #getOrderRelevant()
205 */
206 public void setOrderRelevant(boolean orderRelevant){
207 this.orderRelevant = orderRelevant;
208 }
209
210 //no data status, #2975
211 public NoDescriptiveDataStatus getNoDataStatus() {
212 return noDataStatus;
213 }
214 public void setNoDataStatus(NoDescriptiveDataStatus noDataStatus) {
215 this.noDataStatus = noDataStatus;
216 }
217
218 // ********************* CONVENIENCE ******************************************/
219
220 /**
221 * Convenience method to test the existence of a given state in the state data.
222 * Note: the method ignores modifiers so state data having the state may still be
223 * modified by its modifiers.
224 *
225 * @param state the given {@link State}
226 * @return <code>true</code> if the state exists
227 */
228 public boolean hasState(DefinedTermBase<?> state) {
229 return getStatesOnly().contains(state);
230 }
231
232 /**
233 * Convenience method returning only the list of states. Leaving out modifiers and modifying text.
234 */
235 @Transient
236 public List<eu.etaxonomy.cdm.model.term.DefinedTermBase<?>> getStatesOnly(){
237 List<DefinedTermBase<?>> result = new ArrayList<>();
238 for (StateData stateData : getStateData()){
239 DefinedTermBase<?> state = stateData.getState();
240 if (state != null){
241 result.add(state);
242 }
243 }
244 return result;
245 }
246
247 /**
248 * Convenience method which to set the list of states (no modifiers or modifying text allowed).
249 * All existing state data are removed.
250 * @return
251 */
252 public List<StateData> setStateDataOnly(List<? extends DefinedTermBase<?>> states){
253 List<StateData> stateDataList = new ArrayList<>(getStateData());
254 for (StateData stateData : stateDataList) {
255 removeStateData(stateData);
256 }
257 for (DefinedTermBase<?> state : states) {
258 addStateData(state);
259 }
260 return this.stateData;
261 }
262
263 @Transient
264 @XmlTransient
265 @Override
266 public boolean isCharacterData() {
267 return true;
268 }
269
270 //********************************** toString **************************************/
271
272 @Override
273 public String toString() {
274 return (getFeature()!=null ? getFeature().getLabel(): "") +
275 "[" + stateData +
276 (orderRelevant? ", orderRelevant=" + orderRelevant:"") +
277 (noDataStatus != null ? ", noDataStatus=" + noDataStatus.getLabel() :"")
278 + "]";
279 }
280
281 //*********************************** CLONE *****************************************/
282
283 /**
284 * Clones <i>this</i> categorical data. This is a shortcut that enables to create
285 * a new instance that differs only slightly from <i>this</i> categorical data by
286 * modifying only some of the attributes.
287 * @throws CloneNotSupportedException
288 *
289 * @see eu.etaxonomy.cdm.model.description.DescriptionElementBase#clone()
290 * @see java.lang.Object#clone()
291 */
292 @Override
293 public CategoricalData clone() {
294
295 CategoricalData result = (CategoricalData)super.clone();
296
297 //states
298 result.stateData = new ArrayList<>();
299 for (StateData stateData : getStateData()){
300 StateData newState = stateData.clone();
301 result.addStateData(newState);
302 }
303
304 //no changes to: orderRelevant
305 return result;
306 }
307 }