Project

General

Profile

« Previous | Next » 

Revision 6e858cb7

Added by Andreas Kohlbecker about 6 years ago

ref #7348 moving RegistrationDTO related classes from cdm-vaadin to cdmlib-services

View differences:

cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/dto/EntityReference.java
1
/**
2
* Copyright (C) 2017 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.UUID;
12

  
13
import org.apache.commons.lang.builder.HashCodeBuilder;
14

  
15
public class EntityReference {
16
    UUID uuid;
17
    String label;
18

  
19
    public EntityReference(UUID uuid, String label) {
20
        this.uuid = uuid;
21
        this.label = label;
22
    }
23

  
24

  
25
    public UUID getUuid() {
26
        return uuid;
27
    }
28

  
29
    public String getLabel() {
30
        return label;
31
    }
32

  
33
    /**
34
     * {@inheritDoc}
35
     */
36
    @Override
37
    public int hashCode() {
38
        return new HashCodeBuilder(17, 31)
39
                .append(label)
40
                .append(uuid)
41
                .toHashCode();
42
    }
43

  
44
    /**
45
     * {@inheritDoc}
46
     */
47
    @Override
48
    public boolean equals(Object obj) {
49
        try {
50
            EntityReference other = (EntityReference) obj;
51
            return uuid.equals(other.uuid) && label.equals(other.label);
52

  
53
        } catch (Exception e) {
54
            return false;
55
        }
56
    }
57

  
58
}
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/dto/RegistrationDTO.java
1
/**
2
* Copyright (C) 2017 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.Collection;
13
import java.util.HashSet;
14
import java.util.LinkedHashMap;
15
import java.util.List;
16
import java.util.Set;
17
import java.util.UUID;
18

  
19
import org.apache.commons.lang3.StringUtils;
20
import org.apache.log4j.Logger;
21
import org.joda.time.DateTime;
22

  
23
import eu.etaxonomy.cdm.api.service.exception.RegistrationValidationException;
24
import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager;
25
import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager.TypeDesignationWorkingSet;
26
import eu.etaxonomy.cdm.model.common.TimePeriod;
27
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
28
import eu.etaxonomy.cdm.model.name.Registration;
29
import eu.etaxonomy.cdm.model.name.RegistrationStatus;
30
import eu.etaxonomy.cdm.model.name.TaxonName;
31
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
32
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
33
import eu.etaxonomy.cdm.model.reference.Reference;
34

  
35
public class RegistrationDTO{
36

  
37
    private static final Logger logger = Logger.getLogger(RegistrationDTO.class);
38

  
39
    private String summary = "";
40

  
41
    private RegistrationType registrationType;
42

  
43
    private Reference citation = null;
44

  
45
    private String citationDetail = null;
46

  
47
    private String submitterUserName = null;
48

  
49
    private EntityReference name = null;
50

  
51
    private TypeDesignationSetManager typeDesignationManager;
52

  
53
    private Registration reg;
54

  
55
    private List<String> validationProblems = new ArrayList<>();
56

  
57
    private Set<TypedEntityReference<Registration>> blockedBy;
58

  
59

  
60
    /**
61
     * @param reg
62
     * @param typifiedName should be provided in for Registrations for TypeDesignations
63
     * @throws RegistrationValidationException
64
     */
65
    public RegistrationDTO(Registration reg) {
66

  
67
         this.reg = reg;
68

  
69
         registrationType = RegistrationType.from(reg);
70

  
71
         if(reg.getSubmitter() != null ){
72
             submitterUserName = reg.getSubmitter().getUsername();
73
         }
74

  
75
        if(hasName(reg)){
76
            citation = (Reference) reg.getName().getNomenclaturalReference();
77
            citationDetail = reg.getName().getNomenclaturalMicroReference();
78
            name = new EntityReference(reg.getName().getUuid(), reg.getName().getTitleCache());
79
        }
80
        if(hasTypifications(reg)){
81
            if(!reg.getTypeDesignations().isEmpty()){
82
                for(TypeDesignationBase td : reg.getTypeDesignations()){
83
                    if(citation == null) {
84
                        citation = td.getCitation();
85
                        citationDetail = td.getCitationMicroReference();
86
                    }
87
                }
88
            }
89
        }
90
        switch(registrationType) {
91
        case EMPTY:
92
            summary = "BLANK REGISTRATION";
93
            break;
94
        case NAME:
95
            summary = reg.getName().getTitleCache();
96
            break;
97
        case NAME_AND_TYPIFICATION:
98
        case TYPIFICATION:
99
        default:
100
            try {
101
                typeDesignationManager = new TypeDesignationSetManager(reg.getTypeDesignations());
102
                summary = typeDesignationManager.buildString().print();
103
            } catch (RegistrationValidationException e) {
104
                validationProblems.add("Validation errors: " + e.getMessage());
105
            }
106
            break;
107
        }
108

  
109
        // trigger initialization of the reference
110
        getNomenclaturalCitationString();
111

  
112
    }
113

  
114
    /**
115
     * To create an initially empty DTO for which only the <code>typifiedName</code> and the <code>publication</code> are defined.
116
     * All TypeDesignations added to the <code>Registration</code> need to refer to the same <code>typifiedName</code> and must be
117
     * published in the same <code>publication</code>.
118
     *
119
     * @param reg
120
     * @param typifiedName
121
     */
122
    public RegistrationDTO(Registration reg, TaxonName typifiedName, Reference publication) {
123
        this.reg = reg;
124
        citation = publication;
125
        // create a TypeDesignationSetManager with only a reference to the typifiedName for validation
126
        typeDesignationManager = new TypeDesignationSetManager(typifiedName);
127
    }
128

  
129
    /**
130
     * @param reg
131
     * @return
132
     */
133
    private boolean hasTypifications(Registration reg) {
134
        return reg.getTypeDesignations() != null && reg.getTypeDesignations().size() > 0;
135
    }
136

  
137
    /**
138
     * @param reg
139
     * @return
140
     */
141
    private boolean hasName(Registration reg) {
142
        return reg.getName() != null;
143
    }
144

  
145

  
146
    /**
147
     * Provides access to the Registration entity this DTO has been build from.
148
     * This method is purposely not a getter to hide the original Registration
149
     * from generic processes which are exposing, binding bean properties.
150
     *IReference
151
     * @return
152
     */
153
    public Registration registration() {
154
        return reg;
155
    }
156

  
157

  
158
    /**
159
     * @return the summary
160
     */
161
    public String getSummary() {
162
        return summary;
163
    }
164

  
165
    public String getSubmitterUserName(){
166
        return submitterUserName;
167
    }
168

  
169
    /**
170
     * @return the registrationType
171
     */
172
    public RegistrationType getRegistrationType() {
173
        return registrationType;
174
    }
175

  
176
    /**
177
     * @return the status
178
     */
179
    public RegistrationStatus getStatus() {
180
        return reg.getStatus();
181
    }
182

  
183
    /**
184
     * @return the identifier
185
     */
186
    public String getIdentifier() {
187
        return reg.getIdentifier();
188
    }
189

  
190

  
191
    public UUID getUuid() {
192
        return reg.getUuid();
193
    }
194

  
195
    /**
196
     * @return the specificIdentifier
197
     */
198
    public String getSpecificIdentifier() {
199
        return reg.getSpecificIdentifier();
200
    }
201

  
202
    /**
203
     * @return the registrationDate
204
     */
205
    public DateTime getRegistrationDate() {
206
        return reg.getRegistrationDate();
207
    }
208

  
209
    /**
210
     * @return the registrationDate
211
     */
212
    public TimePeriod getDatePublished() {
213
        return citation == null ? null : citation.getDatePublished();
214
    }
215

  
216
    /**
217
     * @return the created
218
     */
219
    public DateTime getCreated() {
220
        return reg.getCreated();
221
    }
222

  
223
    public Reference getCitation() {
224
        return citation;
225
    }
226

  
227
    public void setCitation(Reference citation) throws Exception {
228
        if(this.citation == null){
229
            this.citation = citation;
230
        } else {
231
            throw new Exception("Can not set the citation on a non emtpy RegistrationDTO");
232
        }
233
    }
234

  
235

  
236
    public UUID getCitationUuid() {
237
        return citation == null ? null : citation.getUuid();
238
    }
239

  
240
    public EntityReference getTypifiedNameRef() {
241
        return typeDesignationManager != null ? typeDesignationManager.getTypifiedNameRef() : null;
242
    }
243

  
244
    public TaxonName getTypifiedName() {
245
        return typeDesignationManager != null ? typeDesignationManager.getTypifiedName() : null;
246
    }
247

  
248
    public EntityReference getNameRef() {
249
        return name;
250
    }
251

  
252
    public LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> getOrderdTypeDesignationWorkingSets() {
253
        return typeDesignationManager != null ? typeDesignationManager.getOrderdTypeDesignationWorkingSets() : null;
254
    }
255

  
256
    /**
257
     * @param baseEntityReference
258
     */
259
    public TypeDesignationWorkingSet getTypeDesignationWorkingSet(TypedEntityReference baseEntityReference) {
260
        return typeDesignationManager != null ? typeDesignationManager.getOrderdTypeDesignationWorkingSets().get(baseEntityReference) : null;
261

  
262
    }
263

  
264
    /**
265
     * @param baseEntityReference
266
     */
267
    public Set<TypeDesignationBase> getTypeDesignationsInWorkingSet(TypedEntityReference baseEntityReference) {
268
        Set<TypeDesignationBase> typeDesignations = new HashSet<>();
269
        TypeDesignationWorkingSet workingSet = getTypeDesignationWorkingSet(baseEntityReference);
270
        for(EntityReference ref :  workingSet.getTypeDesignations()){
271
            typeDesignations.add(findTypeDesignation(ref));
272
        }
273
        return typeDesignations;
274
    }
275

  
276
    public NameTypeDesignation getNameTypeDesignation(TypedEntityReference baseEntityReference) {
277
        Set<TypeDesignationBase> typeDesignations = getTypeDesignationsInWorkingSet(baseEntityReference);
278
        if(typeDesignations.size() == 1){
279
            TypeDesignationBase item = typeDesignations.iterator().next();
280
            return (NameTypeDesignation)item ;
281
        }
282
        if(typeDesignations.size() == 0){
283
            return null;
284
        }
285
        if(typeDesignations.size() > 1){
286
            throw new RuntimeException("Workingsets of NameTypeDesignations must contain exactly one item.");
287
        }
288
        return null;
289
    }
290

  
291
    /**
292
     * @param ref
293
     * @return
294
     */
295
    private TypeDesignationBase findTypeDesignation(EntityReference ref) {
296
        return typeDesignationManager != null ? typeDesignationManager.findTypeDesignation(ref) : null;
297
    }
298

  
299
    public Collection<TypeDesignationBase> getTypeDesignations() {
300
        return typeDesignationManager != null ? typeDesignationManager.getTypeDesignations() : null;
301
    }
302

  
303
    /**
304
     * @return the citationString
305
     */
306
    public String getNomenclaturalCitationString() {
307
        if(citation == null){
308
            return null;
309
        }
310
        if(INomenclaturalReference.class.isAssignableFrom(citation.getClass())){
311
            return ((INomenclaturalReference)citation).getNomenclaturalCitation(citationDetail);
312
        } else {
313
            logger.error("The citation is not a NomenclaturalReference");
314
            return citation.generateTitle();
315
        }
316
    }
317

  
318
    /**
319
     * @return the citationString
320
     */
321
    public String getBibliographicCitationString() {
322
        if(citation == null){
323
            return null;
324
        } else {
325
            if(StringUtils.isNotEmpty(citationDetail)){
326
                // TODO see https://dev.e-taxonomy.eu/redmine/issues/6623
327
                return citation.generateTitle().replaceAll("\\.$", "") + (StringUtils.isNotEmpty(citationDetail) ? ": " + citationDetail : "");
328
            } else {
329
                return citation.generateTitle();
330

  
331
            }
332

  
333
        }
334
    }
335

  
336
    public boolean isBlocked() {
337
        return reg.getBlockedBy() != null && !reg.getBlockedBy().isEmpty();
338
    }
339

  
340
    /**
341
     * @return the blockedBy
342
     */
343
    public Set<TypedEntityReference<Registration>> getBlockedBy() {
344

  
345
        if(blockedBy == null){
346
            blockedBy = new HashSet<>();
347
            if(reg.getBlockedBy() != null){
348
                for(Registration blockReg : reg.getBlockedBy()){
349
                    blockedBy.add(new TypedEntityReference<Registration>(Registration.class, blockReg.getUuid(), blockReg.getIdentifier()));
350
                }
351
            }
352
        }
353
        return blockedBy;
354
    }
355

  
356
    /**
357
     * @return
358
     */
359
    public List<String> getValidationProblems() {
360
        return validationProblems;
361
    }
362

  
363
}
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/dto/RegistrationType.java
1
/**
2
* Copyright (C) 2017 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 eu.etaxonomy.cdm.model.name.Registration;
12

  
13
/**
14
 * @author a.kohlbecker
15
 * @since Mar 3, 2017
16
 *
17
 */
18
public enum RegistrationType {
19

  
20
    /**
21
     * A <code>Registration</code> for a new name
22
     */
23
    NAME,
24
    /**
25
     * A <code>Registration</code> for a new name and one or more according
26
     * typifications.
27
     */
28
    NAME_AND_TYPIFICATION,
29
    /**
30
     * A <code>Registration</code> for one or more typifications for an
31
     * previously published name.
32
     */
33
    TYPIFICATION,
34
    /**
35
     * A newly created <code>Registration</code> without any name and
36
     * typification.
37
     */
38
    EMPTY;
39

  
40
    /**
41
     * @param reg
42
     * @return
43
     */
44
    public static RegistrationType from(Registration reg) {
45

  
46
        if (reg.getName() != null && reg.getTypeDesignations() != null && reg.getTypeDesignations().size() > 0) {
47
            return NAME_AND_TYPIFICATION;
48
        }
49
        if (reg.getName() != null) {
50
            return NAME;
51
        }
52
        if (reg.getTypeDesignations().size() > 0) {
53
            return TYPIFICATION;
54
        }
55
        return EMPTY;
56
    }
57

  
58
    /**
59
     * @return
60
     */
61
    public boolean isName() {
62
        return NAME.equals(this);
63

  
64
    }
65

  
66
    /**
67
     * @return
68
     */
69
    public boolean isTypification() {
70
        return TYPIFICATION.equals(this);
71
    }
72

  
73
    /**
74
     * @return
75
     */
76
    public boolean isNameAndTypification() {
77
        return NAME_AND_TYPIFICATION.equals(this);
78
    }
79

  
80
    /**
81
     * @return
82
     */
83
    public boolean isEnmpty() {
84
        return EMPTY.equals(this);
85
    }
86

  
87
}
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/dto/TypedEntityReference.java
1
/**
2
* Copyright (C) 2017 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.UUID;
12

  
13
import org.apache.commons.lang.builder.HashCodeBuilder;
14

  
15
/**
16
 * @author a.kohlbecker
17
 * @since Jun 12, 2017
18
 *
19
 */
20
public class TypedEntityReference<T> extends EntityReference {
21

  
22
    /**
23
     * @param uuid
24
     * @param label
25
     */
26
    public TypedEntityReference(Class<T> type, UUID uuid, String label) {
27
        super(uuid, label);
28
        this.type = type;
29
    }
30

  
31
    public TypedEntityReference(Class<T> type, UUID uuid) {
32
        super(uuid, null);
33
        this.type = type;
34
    }
35

  
36
    /**
37
     * @return the type
38
     */
39
    public Class<T> getType() {
40
        return type;
41
    }
42

  
43
    /**
44
     * @param type the type to set
45
     */
46
    public void setType(Class<T> type) {
47
        this.type = type;
48
    }
49

  
50
    private Class<T> type;
51

  
52

  
53

  
54
    /**
55
     * {@inheritDoc}
56
     */
57
    @Override
58
    public int hashCode() {
59
        return new HashCodeBuilder(17, 31)
60
                .append(uuid)
61
                .appendSuper(type.hashCode())
62
                .hashCode();
63
    }
64

  
65
    /**
66
     * {@inheritDoc}
67
     */
68
    @SuppressWarnings("rawtypes")
69
    @Override
70
    public boolean equals(Object obj) {
71
        try {
72
            TypedEntityReference other = (TypedEntityReference) obj;
73
            return uuid.equals(other.uuid) && type.equals(other.type);
74

  
75
        } catch (Exception e) {
76
            return false;
77
        }
78
    }
79

  
80
    @Override
81
    public String toString(){
82
        return type.getSimpleName() + "#" + uuid;
83

  
84
    }
85

  
86
}
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/exception/RegistrationValidationException.java
1
/**
2
* Copyright (C) 2017 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.exception;
10

  
11
import java.util.ArrayList;
12
import java.util.List;
13

  
14
/**
15
 * @author a.kohlbecker
16
 * @since Mar 23, 2017
17
 *
18
 */
19
@SuppressWarnings("serial")
20
public class RegistrationValidationException extends Exception {
21

  
22
    List<String> problems = new ArrayList<>();
23

  
24
    /**
25
     * @param message
26
     */
27
    public RegistrationValidationException(String message, List<String> problems) {
28
        super(message);
29
        this.problems = problems;
30
    }
31

  
32
    @Override
33
    public String getMessage() {
34
        StringBuffer sb = new StringBuffer(super.getMessage()).append(" - Problems:");
35
        problems.forEach(p -> sb.append("- ").append(p).append("\n"));
36
        return sb.toString();
37
    }
38

  
39
    public List<String> getProblems() {
40
        return problems;
41
    }
42

  
43

  
44
}
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/name/TypeDesignationSetManager.java
1
/**
2
* Copyright (C) 2017 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.name;
10

  
11
import java.util.ArrayList;
12
import java.util.Arrays;
13
import java.util.Collection;
14
import java.util.Collections;
15
import java.util.Comparator;
16
import java.util.HashMap;
17
import java.util.LinkedHashMap;
18
import java.util.LinkedList;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Optional;
22
import java.util.Set;
23

  
24
import org.hibernate.search.hcore.util.impl.HibernateHelper;
25

  
26
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
27
import eu.etaxonomy.cdm.api.service.dto.EntityReference;
28
import eu.etaxonomy.cdm.api.service.dto.TypedEntityReference;
29
import eu.etaxonomy.cdm.api.service.exception.RegistrationValidationException;
30
import eu.etaxonomy.cdm.model.common.CdmBase;
31
import eu.etaxonomy.cdm.model.common.TermVocabulary;
32
import eu.etaxonomy.cdm.model.common.VersionableEntity;
33
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
34
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
35
import eu.etaxonomy.cdm.model.name.TaxonName;
36
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
37
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
38
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
39
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
40
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
41
/**
42
 * Manages a collection of {@link TypeDesignationBase TypeDesignations} for the same typified name.
43
 *
44
 * Type designations are ordered by the base type which is a {@link TaxonName} for {@link NameTypeDesignation NameTypeDesignations} or
45
 * in case of {@link SpecimenTypeDesignation SpecimenTypeDesignations} the  associate {@link FieldUnit} or the {@link DerivedUnit}
46
 * if the former is missing. The type designations per base type are furthermore ordered by the {@link TypeDesignationStatusBase}.
47
 *
48
 * The TypeDesignationSetManager also provides string representations of the whole ordered set of all
49
 * {@link TypeDesignationBase TypeDesignations} and of the TypeDesignationWorkingSets:
50
 * <ul>
51
 *  <li>{@link #print()})
52
 *  <li>{@link #getOrderdTypeDesignationWorkingSets()} ... {@link TypeDesignationWorkingSet#getRepresentation()}
53
 * </ul>
54
 * Prior using the representations you need to trigger their generation by calling {@link #buildString()}
55
 *
56
 * @author a.kohlbecker
57
 * @since Mar 10, 2017
58
 *
59
 */
60
public class TypeDesignationSetManager {
61

  
62
    enum NameTypeBaseEntityType{
63

  
64
        NAME_TYPE_DESIGNATION,
65
        TYPE_NAME;
66

  
67
    }
68

  
69
    private static final String TYPE_STATUS_SEPARATOR = "; ";
70

  
71
    private static final String TYPE_SEPARATOR = "; ";
72

  
73
    private static final String TYPE_DESIGNATION_SEPARATOR = ", ";
74

  
75
    private Collection<TypeDesignationBase> typeDesignations;
76

  
77
    private NameTypeBaseEntityType nameTypeBaseEntityType = NameTypeBaseEntityType.NAME_TYPE_DESIGNATION;
78

  
79
    /**
80
     * Groups the EntityReferences for each of the TypeDesignations by the according TypeDesignationStatus.
81
     * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
82
     */
83
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderedByTypesByBaseEntity;
84

  
85
    private EntityReference typifiedNameRef;
86

  
87
    private TaxonName typifiedName;
88

  
89
    private String finalString = null;
90

  
91
    final NullTypeDesignationStatus NULL_STATUS = new NullTypeDesignationStatus();
92

  
93
    private List<String> probelms = new ArrayList<>();
94

  
95
    private boolean printCitation = false;
96

  
97
    /**
98
     * @param containgEntity
99
     * @param taxonName
100
     * @throws RegistrationValidationException
101
     *
102
     */
103
    public TypeDesignationSetManager(Collection<TypeDesignationBase> typeDesignations) throws RegistrationValidationException {
104
        this.typeDesignations = typeDesignations;
105
        findTypifiedName();
106
        mapAndSort();
107
    }
108

  
109
    /**
110
     * @param typifiedName2
111
     */
112
    public TypeDesignationSetManager(TaxonName typifiedName) {
113
        this.typeDesignations = new ArrayList<>();
114
        this.typifiedNameRef = new EntityReference(typifiedName.getUuid(), typifiedName.getTitleCache());
115
    }
116

  
117
    /**
118
     * Add one or more TypeDesignations to the manager. This causes re-grouping and re-ordering
119
     * of all managed TypeDesignations.
120
     *
121
     * @param containgEntity
122
     * @param typeDesignations
123
     */
124
    public void addTypeDesigations(CdmBase containgEntity, TypeDesignationBase ... typeDesignations){
125
       this.typeDesignations.addAll(Arrays.asList(typeDesignations));
126
       mapAndSort();
127
    }
128

  
129
    /**
130
     * Groups and orders all managed TypeDesignations.
131
     *
132
     * @param containgEntity
133
     */
134
    protected void mapAndSort() {
135
        finalString = null;
136
        Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus = new HashMap<>();
137
        this.typeDesignations.forEach(td -> mapTypeDesignation(byBaseEntityByTypeStatus, td));
138
        orderedByTypesByBaseEntity = orderByTypeByBaseEntity(byBaseEntityByTypeStatus);
139
    }
140

  
141

  
142
    /**
143
     *
144
     * @param containgEntity
145
     * @param byBaseEntityByTypeStatus
146
     * @param td
147
     */
148
    private void mapTypeDesignation(Map<TypedEntityReference, TypeDesignationWorkingSet> byBaseEntityByTypeStatus,
149
            TypeDesignationBase<?> td){
150

  
151
        TypeDesignationStatusBase<?> status = td.getTypeStatus();
152

  
153
        try {
154
            final VersionableEntity baseEntity = baseEntity(td);
155
            final TypedEntityReference<VersionableEntity> baseEntityReference = makeEntityReference(baseEntity);
156

  
157
            EntityReference typeDesignationEntityReference = new EntityReference(td.getUuid(), stringify(td));
158

  
159
            TypeDesignationWorkingSet typedesignationWorkingSet;
160
            if(!byBaseEntityByTypeStatus.containsKey(baseEntityReference)){
161
                byBaseEntityByTypeStatus.put(baseEntityReference, new TypeDesignationWorkingSet(baseEntity, baseEntityReference));
162
            }
163

  
164
            typedesignationWorkingSet = byBaseEntityByTypeStatus.get(baseEntityReference);
165
            typedesignationWorkingSet.insert(status, typeDesignationEntityReference);
166
        } catch (DataIntegrityException e){
167
            probelms.add(e.getMessage());
168
        }
169
    }
170

  
171
    /**
172
     * @param td
173
     * @return
174
     * @throws DataIntegrityException
175
     */
176
    protected VersionableEntity baseEntity(TypeDesignationBase<?> td) throws DataIntegrityException {
177

  
178
        VersionableEntity baseEntity = null;
179
        if(td  instanceof SpecimenTypeDesignation){
180
            SpecimenTypeDesignation std = (SpecimenTypeDesignation) td;
181
            FieldUnit fu = findFieldUnit(std);
182
            if(fu != null){
183
                baseEntity = fu;
184
            } else if(((SpecimenTypeDesignation) td).getTypeSpecimen() != null){
185
                baseEntity = ((SpecimenTypeDesignation) td).getTypeSpecimen();
186
            }
187
        } else if(td instanceof NameTypeDesignation){
188
            if(nameTypeBaseEntityType == NameTypeBaseEntityType.NAME_TYPE_DESIGNATION){
189
                baseEntity = td;
190
            } else {
191
                // only other option is TaxonName
192
                baseEntity = ((NameTypeDesignation)td).getTypeName();
193
            }
194
        }
195
        if(baseEntity == null) {
196
            throw new DataIntegrityException("Incomplete TypeDesignation, no type missin in " + td.toString());
197
        }
198
        return baseEntity;
199
    }
200

  
201
    /**
202
     * @param td
203
     * @return
204
     */
205
    protected TypedEntityReference<VersionableEntity> makeEntityReference(VersionableEntity baseEntity) {
206

  
207
        baseEntity = (VersionableEntity) HibernateHelper.unproxy(baseEntity);
208
        String label = "";
209
        if(baseEntity  instanceof FieldUnit){
210
                label = ((FieldUnit)baseEntity).getTitleCache();
211
        }
212

  
213
        TypedEntityReference<VersionableEntity> baseEntityReference = new TypedEntityReference(baseEntity.getClass(), baseEntity.getUuid(), label);
214

  
215
        return baseEntityReference;
216
    }
217

  
218

  
219
    private LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> orderByTypeByBaseEntity(
220
            Map<TypedEntityReference, TypeDesignationWorkingSet> stringsByTypeByBaseEntity){
221

  
222
       // order the FieldUnit TypeName keys
223
       List<TypedEntityReference> baseEntityKeyList = new LinkedList<>(stringsByTypeByBaseEntity.keySet());
224
       Collections.sort(baseEntityKeyList, new Comparator<TypedEntityReference>(){
225
        /**
226
         * Sorts the base entities (TypedEntityReference) in the following order:
227
         *
228
         * 1. FieldUnits
229
         * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
230
         * 3. NameType
231
         *
232
         * {@inheritDoc}
233
         */
234
        @Override
235
        public int compare(TypedEntityReference o1, TypedEntityReference o2) {
236

  
237
            Class type1 = o1.getType();
238
            Class type2 = o2.getType();
239

  
240
            if(!type1.equals(type2)) {
241
                if(type1.equals(FieldUnit.class) || type2.equals(FieldUnit.class)){
242
                    // FieldUnits first
243
                    return type1.equals(FieldUnit.class) ? -1 : 1;
244
                } else {
245
                    // name types last (in case of missing FieldUnit we expect the base type to be DerivedUnit which comes into the middle)
246
                    return type2.equals(TaxonName.class) || type2.equals(NameTypeDesignation.class) ? -1 : 1;
247
                }
248
            } else {
249
                return o1.getLabel().compareTo(o2.getLabel());
250
            }
251
        }});
252

  
253
       // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
254
       LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> stringsOrderedbyBaseEntityOrderdByType = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
255

  
256
       for(TypedEntityReference baseEntityRef : baseEntityKeyList){
257

  
258
           TypeDesignationWorkingSet typeDesignationWorkingSet = stringsByTypeByBaseEntity.get(baseEntityRef);
259
           // order the TypeDesignationStatusBase keys
260
            List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(typeDesignationWorkingSet.keySet());
261
            Collections.sort(keyList, new Comparator<TypeDesignationStatusBase>() {
262
                @SuppressWarnings("unchecked")
263
                @Override
264
                public int compare(TypeDesignationStatusBase o1, TypeDesignationStatusBase o2) {
265
                    // fix inverted order of cdm terms by -1*
266
                    if(o1 == null && o2 == null || o1 instanceof NullTypeDesignationStatus && o2 instanceof NullTypeDesignationStatus){
267
                        return 0;
268
                    }
269
                    if(o1 == null || o1 instanceof NullTypeDesignationStatus){
270
                        return -1;
271
                    }
272

  
273
                    if(o2 == null || o2 instanceof NullTypeDesignationStatus){
274
                        return 1;
275
                    }
276
                    return -1 * o1.compareTo(o2);
277
                }
278
            });
279
            // new LinkedHashMap for the ordered TypeDesignationStatusBase keys
280
            TypeDesignationWorkingSet orderedStringsByOrderedTypes = new TypeDesignationWorkingSet(
281
                    typeDesignationWorkingSet.getBaseEntity(),
282
                    baseEntityRef);
283
            keyList.forEach(key -> orderedStringsByOrderedTypes.put(key, typeDesignationWorkingSet.get(key)));
284
            stringsOrderedbyBaseEntityOrderdByType.put(baseEntityRef, orderedStringsByOrderedTypes);
285
       }
286

  
287
        return stringsOrderedbyBaseEntityOrderdByType;
288
    }
289

  
290
    /*
291
    private LinkedHashMap<TypedEntityReference, LinkedHashMap<String, Collection<EntityReference>>> buildOrderedRepresentations(){
292

  
293
        orderedStringsByOrderedTypes.keySet().forEach(
294
                key -> orderedRepresentations.put(
295
                        getTypeDesignationStytusLabel(key),
296
                        orderedStringsByOrderedTypes.get(key))
297
                );
298
        return orderedRepresentations;
299
    }
300
*/
301

  
302
    public TypeDesignationSetManager buildString(){
303

  
304
        if(finalString == null){
305

  
306
            finalString = "";
307
            if(getTypifiedNameCache() != null){
308
                finalString += getTypifiedNameCache() + " ";
309
            }
310

  
311
            int typeCount = 0;
312
            if(orderedByTypesByBaseEntity != null){
313
                for(TypedEntityReference baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
314
                    StringBuilder sb = new StringBuilder();
315
                    if(typeCount++ > 0){
316
                        sb.append(TYPE_SEPARATOR);
317
                    }
318
                    boolean isNameTypeDesignation = false;
319
                    if(SpecimenOrObservationBase.class.isAssignableFrom(baseEntityRef.getType())){
320
                        sb.append("Type: ");
321
                    } else {
322
                        sb.append("NameType: ");
323
                        isNameTypeDesignation = true;
324
                    }
325
                    if(!baseEntityRef.getLabel().isEmpty()){
326
                        sb.append(baseEntityRef.getLabel()).append(" ");
327
                    }
328
                    TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
329
                    if(!isNameTypeDesignation ){
330
                        sb.append("(");
331
                    }
332
                    int typeStatusCount = 0;
333
                    for(TypeDesignationStatusBase<?> typeStatus : typeDesignationWorkingSet.keySet()) {
334
                        if(typeStatusCount++  > 0){
335
                            sb.append(TYPE_STATUS_SEPARATOR);
336
                        }
337
                        boolean isPlural = typeDesignationWorkingSet.get(typeStatus).size() > 1;
338
                        if(!typeStatus.equals(NULL_STATUS)) {
339
                            sb.append(typeStatus.getLabel());
340
                            if(isPlural){
341
                                sb.append("s: ");
342
                            } else {
343
                                sb.append(", ");
344
                            }
345
                        }
346
                        int typeDesignationCount = 0;
347
                        for(EntityReference typeDesignationEntityReference : typeDesignationWorkingSet.get(typeStatus)) {
348
                            if(typeDesignationCount++  > 0){
349
                                sb.append(TYPE_DESIGNATION_SEPARATOR);
350
                            }
351
                            sb.append(typeDesignationEntityReference.getLabel());
352
                        }
353
                    }
354
                    if(!isNameTypeDesignation ){
355
                        sb.append(")");
356
                    }
357
                    typeDesignationWorkingSet.setRepresentation(sb.toString());
358
                    finalString += typeDesignationWorkingSet.getRepresentation();
359
                }
360
            }
361
        }
362
        return this;
363
    }
364

  
365
    /**
366
     * FIXME use the validation framework validators and to store the validation problems!!!
367
     *
368
     * @return
369
     * @throws RegistrationValidationException
370
     */
371
    private void findTypifiedName() throws RegistrationValidationException {
372

  
373
        List<String> problems = new ArrayList<>();
374

  
375
        TaxonName typifiedName = null;
376

  
377
        for(TypeDesignationBase<?> typeDesignation : typeDesignations){
378
            typeDesignation.getTypifiedNames();
379
            if(typeDesignation.getTypifiedNames().isEmpty()){
380

  
381
                //TODO instead throw RegistrationValidationException()
382
                problems.add("Missing typifiedName in " + typeDesignation.toString());
383
                continue;
384
            }
385
            if(typeDesignation.getTypifiedNames().size() > 1){
386
              //TODO instead throw RegistrationValidationException()
387
                problems.add("Multiple typifiedName in " + typeDesignation.toString());
388
                continue;
389
            }
390
            if(typifiedName == null){
391
                // remember
392
                typifiedName = typeDesignation.getTypifiedNames().iterator().next();
393
            } else {
394
                // compare
395
                TaxonName otherTypifiedName = typeDesignation.getTypifiedNames().iterator().next();
396
                if(!typifiedName.getUuid().equals(otherTypifiedName.getUuid())){
397
                  //TODO instead throw RegistrationValidationException()
398
                    problems.add("Multiple typifiedName in " + typeDesignation.toString());
399
                }
400
            }
401

  
402
        }
403
        if(!problems.isEmpty()){
404
            // FIXME use the validation framework
405
            throw new RegistrationValidationException("Inconsistent type designations", problems);
406
        }
407

  
408
        if(typifiedName != null){
409
            // ON SUCCESS -------------------
410
            this.typifiedName = typifiedName;
411
            this.typifiedNameRef = new EntityReference(typifiedName.getUuid(), typifiedName.getTitleCache());
412

  
413
        }
414
    }
415

  
416

  
417
    /**
418
     * @return the title cache of the typifying name or <code>null</code>
419
     */
420
    public String getTypifiedNameCache() {
421
        if(typifiedNameRef != null){
422
            return typifiedNameRef.getLabel();
423
        }
424
        return null;
425
    }
426

  
427
    /**
428
     * @return the title cache of the typifying name or <code>null</code>
429
     */
430
    public EntityReference getTypifiedNameRef() {
431

  
432
       return typifiedNameRef;
433
    }
434

  
435
    /**
436
     * @return
437
     */
438
    public Collection<TypeDesignationBase> getTypeDesignations() {
439
        return typeDesignations;
440
    }
441

  
442
    /**
443
     * @param ref
444
     * @return
445
     */
446
    public TypeDesignationBase findTypeDesignation(EntityReference typeDesignationRef) {
447
        for(TypeDesignationBase td : typeDesignations){
448
            if(td.getUuid().equals(typeDesignationRef.getUuid())){
449
                return td;
450
            }
451
        }
452
        // TODO Auto-generated method stub
453
        return null;
454
    }
455

  
456

  
457
    public LinkedHashMap<TypedEntityReference, TypeDesignationWorkingSet> getOrderdTypeDesignationWorkingSets() {
458
        return orderedByTypesByBaseEntity;
459
    }
460

  
461
    /**
462
     * @param td
463
     * @return
464
     */
465
    private String stringify(TypeDesignationBase td) {
466

  
467
        if(td instanceof NameTypeDesignation){
468
            return stringify((NameTypeDesignation)td);
469
        } else {
470
            return stringify((SpecimenTypeDesignation)td, false);
471
        }
472
    }
473

  
474

  
475
    /**
476
     * @param td
477
     * @return
478
     */
479
    protected String stringify(NameTypeDesignation td) {
480

  
481
        StringBuffer sb = new StringBuffer();
482

  
483
        if(td.getTypeName() != null){
484
            sb.append(td.getTypeName().getTitleCache());
485
        }
486
        if(td.getCitation() != null){
487
            sb.append(" ").append(td.getCitation().getTitleCache());
488
            if(td.getCitationMicroReference() != null){
489
                sb.append(":").append(td.getCitationMicroReference());
490
            }
491
        }
492
        if(td.isNotDesignated()){
493
            sb.append(" not designated");
494
        }
495
        if(td.isRejectedType()){
496
            sb.append(" rejected");
497
        }
498
        if(td.isConservedType()){
499
            sb.append(" conserved");
500
        }
501
        return sb.toString();
502
    }
503

  
504
    /**
505
     * @param td
506
     * @return
507
     */
508
    private String stringify(SpecimenTypeDesignation td, boolean useFullTitleCache) {
509
        String  result = "";
510

  
511
        if(useFullTitleCache){
512
            if(td.getTypeSpecimen() != null){
513
                String nameTitleCache = td.getTypeSpecimen().getTitleCache();
514
                if(getTypifiedNameCache() != null){
515
                    nameTitleCache = nameTitleCache.replace(getTypifiedNameCache(), "");
516
                }
517
                result += nameTitleCache;
518
            }
519
        } else {
520
            if(td.getTypeSpecimen() != null){
521
                DerivedUnit du = td.getTypeSpecimen();
522
                if(du.isProtectedTitleCache()){
523
                    result += du.getTitleCache();
524
                } else {
525
                    DerivedUnitFacadeCacheStrategy cacheStrategy = new DerivedUnitFacadeCacheStrategy();
526
                    result += cacheStrategy.getTitleCache(du, true);
527
                }
528
            }
529
        }
530

  
531
        if(isPrintCitation() && td.getCitation() != null){
532
            if(td.getCitation().getAbbrevTitle() != null){
533
                result += " " + td.getCitation().getAbbrevTitle();
534
            } else {
535
                result += " " + td.getCitation().getTitleCache();
536
            }
537
            if(td.getCitationMicroReference() != null){
538
                result += " :" + td.getCitationMicroReference();
539
            }
540
        }
541
        if(td.isNotDesignated()){
542
            result += " not designated";
543
        }
544

  
545
        return result;
546
    }
547

  
548
    /**
549
     * @param td
550
     * @return
551
     * @deprecated
552
     */
553
    @Deprecated
554
    private FieldUnit findFieldUnit(SpecimenTypeDesignation td) {
555

  
556
        DerivedUnit du = td.getTypeSpecimen();
557
        return findFieldUnit(du);
558
    }
559

  
560
    private FieldUnit findFieldUnit(DerivedUnit du) {
561

  
562
        if(du == null || du.getOriginals() == null){
563
            return null;
564
        }
565
        @SuppressWarnings("rawtypes")
566
        Set<SpecimenOrObservationBase> originals = du.getDerivedFrom().getOriginals();
567
        @SuppressWarnings("rawtypes")
568
        Optional<SpecimenOrObservationBase> fieldUnit = originals.stream()
569
                .filter(original -> original instanceof FieldUnit).findFirst();
570
        if (fieldUnit.isPresent()) {
571
            return (FieldUnit) fieldUnit.get();
572
        } else {
573
            for (@SuppressWarnings("rawtypes")
574
            SpecimenOrObservationBase sob : originals) {
575
                if (sob instanceof DerivedUnit) {
576
                    FieldUnit fu = findFieldUnit((DerivedUnit) sob);
577
                    if (fu != null) {
578
                        return fu;
579
                    }
580
                }
581
            }
582
        }
583

  
584
        return null;
585
    }
586

  
587
    public String print() {
588
        return finalString.trim();
589
    }
590

  
591
    /**
592
     * @return the printCitation
593
     */
594
    public boolean isPrintCitation() {
595
        return printCitation;
596
    }
597

  
598
    /**
599
     * @param printCitation the printCitation to set
600
     */
601
    public void setPrintCitation(boolean printCitation) {
602
        this.printCitation = printCitation;
603
    }
604

  
605
    /**
606
     * @return the typifiedName
607
     */
608
    public TaxonName getTypifiedName() {
609
        return typifiedName;
610
    }
611

  
612
    public void setNameTypeBaseEntityType(NameTypeBaseEntityType nameTypeBaseEntityType){
613
        this.nameTypeBaseEntityType = nameTypeBaseEntityType;
614
    }
615

  
616
    public NameTypeBaseEntityType getNameTypeBaseEntityType(){
617
        return nameTypeBaseEntityType;
618
    }
619

  
620
    /**
621
     * TypeDesignations which refer to the same FieldUnit (SpecimenTypeDesignation) or TaxonName
622
     * (NameTypeDesignation) form a working set. The <code>TypeDesignationWorkingSet</code> internally
623
     * works with EnityReferences to the actual TypeDesignations.
624
     *
625
     * The EntityReferences for TypeDesignations are grouped by the according TypeDesignationStatus.
626
     * The TypeDesignationStatusBase keys can be ordered by the term order defined in the vocabulary.
627
     *
628
     * A workingset can be referenced by the <code>baseEntityReference</code>.
629
     */
630
    public class TypeDesignationWorkingSet extends LinkedHashMap<TypeDesignationStatusBase<?>, Collection<EntityReference>> {
631

  
632
        private static final long serialVersionUID = -1329007606500890729L;
633

  
634
        String workingSetRepresentation = null;
635

  
636
        TypedEntityReference<VersionableEntity> baseEntityReference;
637

  
638
        VersionableEntity baseEntity;
639

  
640
        List<DerivedUnit> derivedUnits = null;
641

  
642
        /**
643
         * @param baseEntityReference
644
         */
645
        public TypeDesignationWorkingSet(VersionableEntity baseEntity, TypedEntityReference<VersionableEntity> baseEntityReference) {
646
            this.baseEntity = baseEntity;
647
            this.baseEntityReference = baseEntityReference;
648
        }
649

  
650
        /**
651
         * @return
652
         */
653
        public VersionableEntity getBaseEntity() {
654
            return baseEntity;
655
        }
656

  
657
        public List<EntityReference> getTypeDesignations() {
658
            List<EntityReference> typeDesignations = new ArrayList<>();
659
            this.values().forEach(typeDesignationReferences -> typeDesignationReferences.forEach(td -> typeDesignations.add(td)));
660
            return typeDesignations;
661
        }
662

  
663
        /**
664
         * @param status
665
         * @param typeDesignationEntityReference
666
         */
667
        public void insert(TypeDesignationStatusBase<?> status, EntityReference typeDesignationEntityReference) {
668

  
669
            if(status == null){
670
                status = NULL_STATUS;
671
            }
672
            if(!containsKey(status)){
673
                put(status, new ArrayList<EntityReference>());
674
            }
675
            get(status).add(typeDesignationEntityReference);
676
        }
677

  
678

  
679
        public String getRepresentation() {
680
            return workingSetRepresentation;
681
        }
682

  
683
        public void setRepresentation(String representation){
684
            this.workingSetRepresentation = representation;
685
        }
686

  
687
        /**
688
         * A reference to the entity which is the common base entity for all TypeDesignations in this workingset.
689
         * For a {@link SpecimenTypeDesignation} this is usually the {@link FieldUnit} if it is present. Otherwise it can also be
690
         * a {@link DerivedUnit} or something else depending on the specific use case.
691
         *
692
         * @return the baseEntityReference
693
         */
694
        public TypedEntityReference<VersionableEntity> getBaseEntityReference() {
695
            return baseEntityReference;
696
        }
697

  
698
        @Override
699
        public String toString(){
700
            if(workingSetRepresentation != null){
701
                return workingSetRepresentation;
702
            } else {
703
                return super.toString();
704
            }
705
        }
706

  
707
        /**
708
         * @return
709
         */
710
        public boolean isSpecimenTypeDesigationWorkingSet() {
711
            return SpecimenOrObservationBase.class.isAssignableFrom(baseEntityReference.getType());
712
        }
713

  
714
        public TypeDesignationWorkingSetType getWorkingsetType() {
715
            return isSpecimenTypeDesigationWorkingSet() ? TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET : TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET;
716
        }
717

  
718
    }
719

  
720
    public enum TypeDesignationWorkingSetType {
721
        SPECIMEN_TYPE_DESIGNATION_WORKINGSET,
722
        NAME_TYPE_DESIGNATION_WORKINGSET,
723
    }
724

  
725
    @SuppressWarnings({ "deprecation", "serial" })
726
    class NullTypeDesignationStatus extends TypeDesignationStatusBase<NullTypeDesignationStatus>{
727

  
728
        /**
729
         * {@inheritDoc}
730
         */
731
        @Override
732
        public void resetTerms() {
733
            // empty
734

  
735
        }
736

  
737
        /**
738
         * {@inheritDoc}
739
         */
740
        @Override
741
        protected void setDefaultTerms(TermVocabulary<NullTypeDesignationStatus> termVocabulary) {
742
            // empty
743
        }
744

  
745
    }
746

  
747
    class DataIntegrityException extends Exception {
748

  
749
        private static final long serialVersionUID = 1464726696296824905L;
750

  
751
        /**
752
         * @param string
753
         */
754
        public DataIntegrityException(String string) {
755
            super(string);
756
        }
757
    }
758
}

Also available in: Unified diff