ref #4866, ref #9228 implement subtree clone
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / dto / TaxonRelationshipsDTO.java
1 /**
2 * Copyright (C) 2018 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.util.ArrayList;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.UUID;
15
16 import org.joda.time.DateTime;
17
18 import eu.etaxonomy.cdm.common.CdmUtils;
19 import eu.etaxonomy.cdm.format.taxon.TaxonRelationshipFormatter;
20 import eu.etaxonomy.cdm.model.common.Language;
21 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
22 import eu.etaxonomy.cdm.model.taxon.Taxon;
23 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
24 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
25 import eu.etaxonomy.cdm.model.term.Representation;
26 import eu.etaxonomy.cdm.persistence.dto.TermDto;
27 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
28 import eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper;
29 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
30
31 /**
32 * DTO to transfer a list of taxon relationships for a given taxon.
33 *
34 * @author a.mueller
35 * @since 15.08.2018
36 */
37 public class TaxonRelationshipsDTO {
38
39 private static final String SENSU_SEPARATOR = ", ";
40
41 public class TaxonRelationDTO{
42
43 private boolean doubtful = false;
44 private boolean misapplication = false;
45 private boolean synonym = false;
46 private Direction direction;
47 private UUID taxonUuid;
48 private String cache;
49 private List<TaggedText> taggedText;
50 //TODO maybe this will be changed in future
51 private TermDto type;
52 private UUID typeUuid;
53 private SourceDTO sec;
54 private SourceDTO relSec;
55
56
57 public TaxonRelationDTO(TaxonRelationship relation, Direction direction, List<Language> languages) {
58 Taxon relatedTaxon = direction == Direction.relatedTo? relation.getToTaxon()
59 : relation.getFromTaxon();
60 this.taxonUuid = relatedTaxon.getUuid();
61 this.doubtful = relation.isDoubtful();
62 this.direction = direction;
63 TaxonRelationshipType relType = relation.getType();
64
65 if (relType != null){
66 this.misapplication = relType.isMisappliedName();
67 this.synonym = relType.isAnySynonym();
68 this.typeUuid = relType.getUuid();
69 // TODO there must be a better DTO which also includes
70 Set<Representation> representations = direction.isDirect() ? relType.getRepresentations() : relType.getInverseRepresentations();
71 TermDto termDto = TermDto.fromTerm(relType, representations);
72 this.type = termDto;
73 // TODO localize
74 // termDto.localize(representation_L10n);
75 }
76 List<TaggedText> tags = new TaxonRelationshipFormatter().getTaggedText(
77 relation, direction == Direction.relatedFrom, languages);
78 this.taggedText = tags;
79 this.setCache(TaggedCacheHelper.createString(tags));
80 }
81
82
83 public UUID getTaxonUuid() {
84 return taxonUuid;
85 }
86 public void setTaxonUuid(UUID taxonUuid) {
87 this.taxonUuid = taxonUuid;
88 }
89 public boolean isDoubtful() {
90 return doubtful;
91 }
92 public void setDoubtful(boolean doubtful) {
93 this.doubtful = doubtful;
94 }
95
96 public Direction getDirection() {
97 return direction;
98 }
99 public void setDirection(Direction direction) {
100 this.direction = direction;
101 }
102
103
104
105 @Override
106 public String toString(){
107 return taxonUuid == null? super.toString() : taxonUuid.toString();
108 }
109
110 public String getCache() {
111 return cache;
112 }
113 public void setCache(String cache) {
114 this.cache = cache;
115 }
116
117 public List<TaggedText> getTaggedText() {
118 return taggedText;
119 }
120 // public void setTaggedText(List<TaggedText> taggedText) {
121 // this.taggedText = taggedText;
122 // }
123
124 public boolean isMisapplication() {
125 return misapplication;
126 }
127 public void setMisapplication(boolean misapplication) {
128 this.misapplication = misapplication;
129 }
130
131 public boolean isSynonym() {
132 return synonym;
133 }
134 public void setSynonym(boolean synonym) {
135 this.synonym = synonym;
136 }
137
138 public TermDto getType() {
139 return type;
140 }
141 public void setType(TermDto type) {
142 this.type = type;
143 }
144
145
146 /**
147 * @return the typeUuid
148 */
149 public UUID getTypeUuid() {
150 return typeUuid;
151 }
152
153
154 /**
155 * @param typeUuid the typeUuid to set
156 */
157 public void setTypeUuid(UUID typeUuid) {
158 this.typeUuid = typeUuid;
159 }
160
161 }
162
163 private List<TaxonRelationDTO> relations = new ArrayList<>();
164
165 private List<List<TaggedText>> misapplications = new ArrayList<>();
166
167 private DateTime date = DateTime.now();
168
169 //** ******************* CONSTRUCTOR **************************/
170
171 public TaxonRelationshipsDTO() {}
172
173 // public TaxonRelationshipsDTO(UUID taxonUuid) {
174 // IncludedTaxon originalTaxon = new TaxonRelationshipsDTO(taxonUuid, false);
175 // includedTaxa.add(originalTaxon);
176 // }
177
178 // ************************** GETTER / SETTER ***********************/
179
180 public List<TaxonRelationDTO> getRelations() {
181 return relations;
182 }
183
184 public void setIncludedTaxa(List<TaxonRelationDTO> relations) {
185 this.relations = relations;
186 }
187
188 public void addRelation(TaxonRelationDTO relation){
189 relations.add(relation);
190 }
191
192 public TaxonRelationDTO addRelation(TaxonRelationship relation, Direction direction, List<Language> languages) {
193 TaxonRelationDTO newRelation = new TaxonRelationDTO(relation, direction, languages);
194 relations.add(newRelation);
195 return newRelation;
196 }
197
198 public void createMisapplicationString() {
199 List<List<TaggedText>> result = new ArrayList<>();
200
201 for (TaxonRelationDTO relation: relations){
202 if (relation.isMisapplication()){
203 List<TaggedText> tags = relation.getTaggedText();
204
205 boolean isDuplicate = false;
206 for (List<TaggedText> existing: result){
207 isDuplicate = mergeIfDuplicate(existing, tags);
208 if (isDuplicate){
209 break;
210 }
211 }
212 if (!isDuplicate){
213 List<TaggedText> newTags = new ArrayList<>(tags.size());
214 newTags.addAll(tags);
215 result.add(newTags);
216 }
217 }
218 }
219 this.setMisapplications(result);
220 }
221
222 /**
223 * Checks if all tags are equal, except for the sensuReference tags
224 * @param existing
225 * @param tags
226 * @return
227 */
228 private boolean mergeIfDuplicate(List<TaggedText> first, List<TaggedText> second) {
229 int i = 0;
230 int j = 0;
231 int sensuEndInFirst = -1;
232 int sensuStartInSecond = -1;
233 int senusEndInSecond = -1;
234
235 boolean sensuHandled = false;
236 boolean isDuplicate = true;
237 while (isDuplicate && i < first.size() && j< second.size()){
238 if (tagEqualsMisapplied(first.get(i), second.get(i))){
239 i++;j++;
240 }else if (!sensuHandled){
241 while (i < first.size() && tagIsSensu(first.get(i))){
242 i++;
243 sensuEndInFirst = i;
244 }
245 sensuStartInSecond = j;
246 while (j< second.size() && tagIsSensu(second.get(j))){
247 j++;
248 senusEndInSecond = j;
249 }
250 sensuHandled = true;
251 }else{
252 isDuplicate = false;
253 i++;j++; //not really necessary
254 }
255
256 }
257 isDuplicate = isDuplicate && i == first.size() && j == second.size();
258 if (isDuplicate && sensuEndInFirst > -1 && sensuStartInSecond > -1){
259 first.addAll(sensuEndInFirst, second.subList(sensuStartInSecond, senusEndInSecond));
260 first.add(sensuEndInFirst, TaggedText.NewSeparatorInstance(SENSU_SEPARATOR));
261 return true;
262 }else{
263 return false;
264 }
265 }
266
267 private boolean tagIsSensu(TaggedText tag) {
268 if (tag.getType() == TagEnum.secReference ||
269 tag.getType() == TagEnum.secMicroReference ||
270 isSensuSeparator(tag)){
271 return true;
272 }
273 return false;
274 }
275
276 private boolean isSensuSeparator(TaggedText tag) {
277 if (SENSU_SEPARATOR.equals(tag.getText())
278 && tag.getType() == TagEnum.separator) {
279 return true;
280 }
281 return false;
282 }
283
284 private boolean tagEqualsMisapplied(TaggedText x, TaggedText y) {
285 if (CdmUtils.nullSafeEqual(x.getText(),y.getText())
286 && x.getType().equals(y.getType())
287 && CdmUtils.nullSafeEqual(x.getEntityReference(),y.getEntityReference())){
288 return true;
289 }else{
290 return false;
291 }
292 }
293
294 public DateTime getDate() {
295 return date;
296 }
297 public void setDate(DateTime date) {
298 this.date = date;
299 }
300
301 public int getSize(){
302 return relations.size();
303 }
304 //
305 // public boolean contains(UUID taxonUuid) {
306 // for (TaxonRelationDTO relation: relations){
307 // if (taxon.taxonUuid.equals(taxonUuid)){
308 // return true;
309 // }
310 // }
311 // return false;
312 // }
313
314 @Override
315 public String toString(){
316 String result = "";
317 for (TaxonRelationDTO relation : relations){
318 result += relation.toString() + ",";
319 }
320 if (result.length() > 0){
321 result = result.substring(0, result.length() - 1);
322 }
323
324 result = "[" + result + "]";
325 return result;
326 }
327
328 public List<List<TaggedText>> getMisapplications() {
329 return misapplications;
330 }
331 public void setMisapplications(List<List<TaggedText>> misapplications) {
332 this.misapplications = misapplications;
333 }
334 }