ref #9772 add deproxy to collector title computation
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / dto / SpecimenOrObservationBaseDTO.java
1 /**
2 * Copyright (C) 2015 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.api.service.dto;
10
11 import java.io.Serializable;
12 import java.util.AbstractMap;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Comparator;
16 import java.util.EnumSet;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.TreeSet;
21 import java.util.stream.Collectors;
22
23 import eu.etaxonomy.cdm.common.CdmUtils;
24 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
25 import eu.etaxonomy.cdm.model.agent.AgentBase;
26 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
27 import eu.etaxonomy.cdm.model.common.CdmBase;
28 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
29 import eu.etaxonomy.cdm.model.description.DescriptionBase;
30 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
31 import eu.etaxonomy.cdm.model.description.Feature;
32 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
33 import eu.etaxonomy.cdm.model.description.TextData;
34 import eu.etaxonomy.cdm.model.media.Media;
35 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
36 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
37 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
38 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
39 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
40 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
41 import eu.etaxonomy.cdm.model.term.DefinedTerm;
42 import eu.etaxonomy.cdm.model.term.TermBase;
43 import eu.etaxonomy.cdm.ref.TypedEntityReference;
44
45
46 public abstract class SpecimenOrObservationBaseDTO extends TypedEntityReference<SpecimenOrObservationBase<?>>{
47
48 private static final long serialVersionUID = -7597690654462090732L;
49
50 private int id;
51 private TreeSet<AbstractMap.SimpleEntry<String, String>> characterData;
52 private DerivationTreeSummaryDTO derivationTreeSummary;
53 protected String taxonName;
54
55 protected String summaryLabel;
56 protected boolean hasDetailImage;
57 private boolean hasCharacterData;
58 private boolean hasDna;
59 private boolean hasSpecimenScan;
60
61 private SpecimenOrObservationType recordBase;
62 private TermBase kindOfUnit;
63 private String collectorsString;
64 private String individualCount;
65 private Set<DerivedUnitDTO> derivatives;
66
67 private Set<SpecimenTypeDesignationDTO> specimenTypeDesignations;
68
69 private EventDTO<DerivationEvent> derivationEvent;
70
71 // TODO use DTO !!!
72 private Set<IdentifiableSource> sources;
73
74 private List<MediaDTO> listOfMedia = new ArrayList<>();
75
76 private DefinedTerm sex;
77
78 private DefinedTerm lifeStage;
79
80 private List<DeterminationEventDTO>determinations;
81
82 protected SpecimenOrObservationBaseDTO(SpecimenOrObservationBase<?> specimenOrObservation) {
83 super(HibernateProxyHelper.getClassWithoutInitializingProxy(specimenOrObservation), specimenOrObservation.getUuid(), specimenOrObservation.getTitleCache());
84 this.id = specimenOrObservation.getId();
85 Set<Media> collectedMedia = collectMedia(specimenOrObservation);
86 addMediaAsDTO(collectedMedia);
87 setKindOfUnit(specimenOrObservation.getKindOfUnit());
88 setSex(specimenOrObservation.getSex());
89 setIndividualCount(specimenOrObservation.getIndividualCount());
90 lifeStage = specimenOrObservation.getLifeStage();
91 FieldUnit fieldUnit = null;
92 if (specimenOrObservation instanceof FieldUnit){
93 fieldUnit = (FieldUnit)specimenOrObservation;
94 }else{
95 fieldUnit = getFieldUnit((DerivedUnit)specimenOrObservation);
96 }
97 if (fieldUnit != null){
98 AgentBase<?> collector = null;
99 if (fieldUnit.getGatheringEvent() != null){
100 collector = fieldUnit.getGatheringEvent().getCollector();
101 }
102 String fieldNumberString = CdmUtils.Nz(fieldUnit.getFieldNumber());
103 if (collector != null){
104 if (collector.isInstanceOf(TeamOrPersonBase.class)){
105 collectorsString = CdmBase.deproxy(collector, TeamOrPersonBase.class).getCollectorTitleCache();
106 }else{
107 collectorsString = collector.getTitleCache(); //institutions
108 }
109 }
110 collectorsString = CdmUtils.concat(" - ", collectorsString, fieldNumberString);
111 }
112 setDeterminations(specimenOrObservation.getDeterminations().stream()
113 .map(det -> DeterminationEventDTO.from(det))
114 .collect(Collectors.toList())
115 );
116 if (specimenOrObservation instanceof DerivedUnit){
117 DerivedUnit derivedUnit = (DerivedUnit)specimenOrObservation;
118 if (derivedUnit.getSpecimenTypeDesignations() != null){
119 setSpecimenTypeDesignations(derivedUnit.getSpecimenTypeDesignations());
120 }
121 }
122 }
123
124 /**
125 * finds the field unit of the derived unit or null if no field unit exist
126 * @param specimenOrObservation
127 * @return
128 */
129 private FieldUnit getFieldUnit(DerivedUnit specimenOrObservation) {
130 if (specimenOrObservation.getDerivedFrom() != null && !specimenOrObservation.getDerivedFrom().getOriginals().isEmpty()){
131 for (SpecimenOrObservationBase<?> specimen: specimenOrObservation.getDerivedFrom().getOriginals()){
132 if (specimen instanceof FieldUnit){
133 return (FieldUnit)specimen;
134 }else if (specimen instanceof DerivedUnit){
135 getFieldUnit(HibernateProxyHelper.deproxy(specimen,DerivedUnit.class));
136 }
137 }
138 }
139 return null;
140 }
141
142 public String getCollectorsString() {
143 return collectorsString;
144 }
145 public void setCollectorsString(String collectorsString) {
146 this.collectorsString = collectorsString;
147 }
148
149 public Set<SpecimenTypeDesignationDTO> getSpecimenTypeDesignations() {
150 return specimenTypeDesignations;
151 }
152
153 public void setSpecimenTypeDesignations(Set<SpecimenTypeDesignation> specimenTypeDesignations) {
154 this.specimenTypeDesignations = new HashSet<>();
155 for (SpecimenTypeDesignation typeDes: specimenTypeDesignations){
156 if (typeDes != null){
157 this.specimenTypeDesignations.add(new SpecimenTypeDesignationDTO(typeDes));
158 }
159 }
160 }
161
162 public Set<IdentifiableSource> getSources() {
163 return sources;
164 }
165
166 public void setSources(Set<IdentifiableSource> sources) {
167 this.sources = sources;
168 }
169
170 public DerivationTreeSummaryDTO getDerivationTreeSummary() {
171 return derivationTreeSummary;
172 }
173 public void setDerivationTreeSummary(DerivationTreeSummaryDTO derivationTreeSummary) {
174 this.derivationTreeSummary = derivationTreeSummary;
175 if(derivationTreeSummary != null) {
176 setHasSpecimenScan(isHasSpecimenScan() || !derivationTreeSummary.getSpecimenScans().isEmpty());
177 setHasDetailImage(isHasDetailImage() || !derivationTreeSummary.getDetailImages().isEmpty());
178 setHasDna(isHasDna() || !derivationTreeSummary.getMolecularDataList().isEmpty());
179 }
180 }
181
182 public TreeSet<AbstractMap.SimpleEntry<String, String>> getCharacterData() {
183 return characterData;
184 }
185 public void addCharacterData(String character, String state){
186 if(characterData==null){
187 characterData = new TreeSet<>(new PairComparator());
188 }
189 characterData.add(new AbstractMap.SimpleEntry<>(character, state));
190 }
191
192 private class PairComparator implements Comparator<AbstractMap.SimpleEntry<String,String>>, Serializable {
193
194 private static final long serialVersionUID = -8589392050761963540L;
195
196 @Override
197 public int compare(AbstractMap.SimpleEntry<String, String> o1, AbstractMap.SimpleEntry<String, String> o2) {
198 if(o1==null && o2!=null){
199 return -1;
200 }
201 if(o1!=null && o2==null){
202 return 1;
203 }
204 if(o1!=null && o2!=null){
205 return o1.getKey().compareTo(o2.getKey());
206 }
207 return 0;
208 }
209 }
210
211 public boolean isHasCharacterData() {
212 return hasCharacterData;
213 }
214 public void setHasCharacterData(boolean hasCharacterData) {
215 this.hasCharacterData = hasCharacterData;
216 }
217
218 public boolean isHasDna() {
219 return hasDna;
220 }
221 public void setHasDna(boolean hasDna) {
222 this.hasDna = hasDna;
223 }
224
225 public boolean isHasDetailImage() {
226 return hasDetailImage;
227 }
228 public void setHasDetailImage(boolean hasDetailImage) {
229 this.hasDetailImage = hasDetailImage;
230 }
231
232 public boolean isHasSpecimenScan() {
233 return hasSpecimenScan;
234 }
235 public void setHasSpecimenScan(boolean hasSpecimenScan) {
236 this.hasSpecimenScan = hasSpecimenScan;
237 }
238 /**
239 * @return The summary of all DerivedUnit identifiers with the label of
240 * this SpecimenOrObservationBase.
241 * This label is usually being user for citing the unit in publications.
242 */
243 public String getSummaryLabel() {
244 return summaryLabel;
245 }
246
247 /**
248 * Summary of all DerivedUnit identifiers with the label of this SpecimenOrObservationBase.
249 * This label is usually being user for citing the unit in publications.
250 */
251 public void setSummaryLabel(String summaryLabel) {
252 this.summaryLabel = summaryLabel;
253 }
254
255 public SpecimenOrObservationType getRecordBase() {
256 return recordBase;
257 }
258 public void setRecordBase(SpecimenOrObservationType specimenOrObservationType) {
259 this.recordBase = specimenOrObservationType;
260 }
261
262 public Set<DerivedUnitDTO> getDerivatives() {
263 if (this.derivatives == null){
264 this.derivatives = new HashSet<>();
265 }
266 return derivatives;
267 }
268
269 public void setDerivatives(Set<DerivedUnitDTO> derivatives) {
270 this.derivatives = derivatives;
271 updateTreeDependantData();
272 }
273
274 public void addDerivative(DerivedUnitDTO derivate){
275 if (this.derivatives == null){
276 this.derivatives = new HashSet<>();
277 }
278 this.derivatives.add(derivate);
279 updateTreeDependantData();
280 }
281 public void addAllDerivatives(Set<DerivedUnitDTO> derivatives){
282 if (this.derivatives == null){
283 this.derivatives = new HashSet<>();
284 }
285 this.derivatives.addAll(derivatives);
286 updateTreeDependantData();
287 }
288
289 /**
290 * To be overwritten by implementing classes to
291 * update data which depends on the derivation tree
292 */
293 protected abstract void updateTreeDependantData();
294
295 /**
296 * Recursively collects all derivatives from this.
297 */
298 public Collection<DerivedUnitDTO> collectDerivatives() {
299 return collectDerivatives(new HashSet<>());
300 }
301
302 /**
303 * private partner method to {@link #collectDerivatives()} for recursive calls.
304 *
305 * @param dtos
306 */
307 private Collection<DerivedUnitDTO> collectDerivatives(Set<DerivedUnitDTO> dtos) {
308 dtos.addAll(getDerivatives());
309 if(derivatives != null) {
310 for(SpecimenOrObservationBaseDTO subDto : derivatives) {
311 dtos.addAll(subDto.collectDerivatives(dtos));
312 }
313 }
314 return dtos;
315 }
316
317 public EventDTO<DerivationEvent> getDerivationEvent() {
318 return derivationEvent;
319 }
320 public void setDerivationEvent(EventDTO<DerivationEvent> derivationEvent) {
321 this.derivationEvent = derivationEvent;
322 }
323
324 public List<MediaDTO> getListOfMedia() {
325 return listOfMedia;
326 }
327 public void setListOfMedia(List<MediaDTO> listOfMedia) {
328 this.listOfMedia = listOfMedia;
329 }
330
331 protected Set<Media> collectMedia(SpecimenOrObservationBase<?> specimenOrObservation){
332 Set<Media> collectedMedia = new HashSet<>();
333 Set<SpecimenDescription> descriptions = specimenOrObservation.getSpecimenDescriptionImageGallery();
334 for (DescriptionBase<?> desc : descriptions){
335 if (desc instanceof SpecimenDescription){
336 SpecimenDescription specimenDesc = (SpecimenDescription)desc;
337 for (DescriptionElementBase element : specimenDesc.getElements()){
338 if (element.isInstanceOf(TextData.class)&& element.getFeature().equals(Feature.IMAGE())) {
339 for (Media media :element.getMedia()){
340 collectedMedia.add(media);
341 }
342 }
343 }
344 }
345 }
346 return collectedMedia;
347 }
348
349 private void addMediaAsDTO(Set<Media> media) {
350 for(Media m : media) {
351 List<MediaDTO> dtos = MediaDTO.fromEntity(m);
352 getListOfMedia().addAll(dtos);
353 }
354 }
355
356 /**
357 * @param sob
358 * The Unit to assemble the derivatives information for
359 * @param maxDepth
360 * The maximum number of derivation events levels up to which derivatives are to be assembled.
361 * <code>NULL</code> means infinitely.
362 * @param includeTypes
363 * Allows for positive filtering by {@link SpecimenOrObservationType}.
364 * Filter is disabled when <code>NULL</code>. This only affects the derivatives assembled in the
365 * {@link #derivatives} list. The <code>unitLabelsByCollection</code> are always collected for the
366 * whole bouquet of derivatives.
367 * @param unitLabelsByCollection
368 * A map to record the unit labels (most significant identifier + collection code) per collection.
369 * Optional parameter, may be <code>NULL</code>.
370 * @return
371 */
372 protected Set<DerivedUnitDTO> assembleDerivatives(SpecimenOrObservationBase<?> sob,
373 Integer maxDepth, EnumSet<SpecimenOrObservationType> includeTypes) {
374
375 boolean doDescend = maxDepth == null || maxDepth > 0;
376 Integer nextLevelMaxDepth = maxDepth != null ? maxDepth - 1 : null;
377 Set<DerivedUnitDTO> derivateDTOs = new HashSet<>();
378 // collectDerivedUnitsMaxdepth => 0 to avoid aggregation of sub ordinate
379 // derivatives at each level
380 Integer collectDerivedUnitsMaxdepth = 0;
381 for (DerivedUnit derivedUnit : sob.collectDerivedUnits(collectDerivedUnitsMaxdepth)) {
382 if(!derivedUnit.isPublish()){
383 continue;
384 }
385
386 if (doDescend && (includeTypes == null || includeTypes.contains(derivedUnit.getRecordBasis()))) {
387 DerivedUnitDTO derivedUnitDTO = DerivedUnitDTO.fromEntity(derivedUnit, nextLevelMaxDepth, includeTypes);
388 derivateDTOs.add(derivedUnitDTO);
389 setHasCharacterData(isHasCharacterData() || derivedUnitDTO.isHasCharacterData());
390 // NOTE! the flags setHasDetailImage, setHasDna, setHasSpecimenScan are also set in
391 // setDerivateDataDTO(), see below
392 setHasDetailImage(isHasDetailImage() || derivedUnitDTO.isHasDetailImage());
393 setHasDna(isHasDna() || derivedUnitDTO.isHasDna());
394 setHasSpecimenScan(isHasSpecimenScan() || derivedUnitDTO.isHasSpecimenScan());
395 }
396 }
397 return derivateDTOs;
398 }
399
400 public TermBase getKindOfUnit() {
401 return kindOfUnit;
402 }
403 public void setKindOfUnit(TermBase kindOfUnit) {
404 this.kindOfUnit = HibernateProxyHelper.deproxy(kindOfUnit);
405 }
406
407 public DefinedTerm getSex() {
408 return sex;
409 }
410 public void setSex(DefinedTerm sex) {
411 this.sex = sex;
412 }
413
414 public DefinedTerm getLifeStage() {
415 return lifeStage;
416 }
417 public void setLifeStage(DefinedTerm lifeStage) {
418 this.lifeStage = lifeStage;
419 }
420
421 public int getId() {
422 return id;
423 }
424
425 public String getIndividualCount() {
426 return individualCount;
427 }
428 public void setIndividualCount(String individualCount) {
429 this.individualCount = individualCount;
430 }
431
432 public List<DeterminationEventDTO> getDeterminations() {
433 return determinations;
434 }
435
436 public void setDeterminations(List<DeterminationEventDTO> determinations) {
437 this.determinations = determinations;
438 }
439 }