Project

General

Profile

Revision 3733da48

ID3733da4802fa9defaa3426f9ae4b182de83c341e
Parent 2bbade98
Child 7b000bdc

Added by Andreas Müller 5 months ago

ref #9703 Improve handling of type designations with no status in TypeDesignationSetManager

View differences:

cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/name/TypeDesignationSetFormatter.java
17 17
import org.apache.commons.lang3.StringUtils;
18 18

  
19 19
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
20
import eu.etaxonomy.cdm.api.service.name.TypeDesignationWorkingSet.TypeDesignationWorkingSetType;
20 21
import eu.etaxonomy.cdm.common.CdmUtils;
21 22
import eu.etaxonomy.cdm.common.UTF8;
22 23
import eu.etaxonomy.cdm.format.reference.OriginalSourceFormatter;
......
31 32
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
32 33
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
33 34
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
34
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
35 35
import eu.etaxonomy.cdm.model.reference.OriginalSourceBase;
36 36
import eu.etaxonomy.cdm.model.reference.Reference;
37 37
import eu.etaxonomy.cdm.ref.TypedEntityReference;
......
94 94
        }
95 95

  
96 96
        int typeSetCount = 0;
97
        LinkedHashMap<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> orderedByTypesByBaseEntity = manager.getOrderedTypeDesignationWorkingSets();
98
        if(orderedByTypesByBaseEntity != null){
97
        LinkedHashMap<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> orderedByTypesByBaseEntity
98
                    = manager.getOrderedTypeDesignationWorkingSets();
99
        TypeDesignationWorkingSetType lastWsType = null;
100
        if (orderedByTypesByBaseEntity != null){
99 101
            for(TypedEntityReference<?> baseEntityRef : orderedByTypesByBaseEntity.keySet()) {
100 102
                buildTaggedTextForSingleTypeSet(manager, withBrackets, finalBuilder,
101
                        typeSetCount, baseEntityRef);
103
                        typeSetCount, baseEntityRef, lastWsType);
104
                lastWsType = orderedByTypesByBaseEntity.get(baseEntityRef).getWorkingsetType();
102 105
                typeSetCount++;
103 106
            }
104 107
        }
......
106 109
    }
107 110

  
108 111
    private void buildTaggedTextForSingleTypeSet(TypeDesignationSetManager manager, boolean withBrackets,
109
            TaggedTextBuilder finalBuilder, int typeSetCount, TypedEntityReference<?> baseEntityRef) {
112
            TaggedTextBuilder finalBuilder, int typeSetCount, TypedEntityReference<?> baseEntityRef, TypeDesignationWorkingSetType lastWsType) {
110 113

  
111 114
        LinkedHashMap<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet>
112 115
                orderedByTypesByBaseEntity = manager.getOrderedTypeDesignationWorkingSets();
116
        TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
113 117

  
114 118
        TaggedTextBuilder workingsetBuilder = new TaggedTextBuilder();
115
        boolean isSpecimenTypeDesignation = SpecimenOrObservationBase.class.isAssignableFrom(baseEntityRef.getType());
116 119
        if(typeSetCount > 0){
117 120
            workingsetBuilder.add(TagEnum.separator, TYPE_SEPARATOR);
118 121
        }else if (withStartingTypeLabel){
......
120 123
            //name types separately, but this is such a rare case (if at all) and
121 124
            //increases complexity so it is not yet implemented
122 125
            boolean isPlural = hasMultipleTypes(orderedByTypesByBaseEntity);
123
            if(isSpecimenTypeDesignation){
126
            if(typeDesignationWorkingSet.getWorkingsetType().isSpecimenType()){
124 127
                workingsetBuilder.add(TagEnum.label, (isPlural? "Types:": "Type:"));
125
            } else if (NameTypeDesignation.class.isAssignableFrom(baseEntityRef.getType())){
128
            } else if (typeDesignationWorkingSet.getWorkingsetType().isNameType()){
126 129
                workingsetBuilder.add(TagEnum.label, (isPlural? "Nametypes:": "Nametype:"));
127 130
            } else {
128 131
                //do nothing for now
129 132
            }
130 133
        }
131 134

  
132
        boolean hasExplicitBaseEntity = hasExplicitBaseEntity(baseEntityRef, orderedByTypesByBaseEntity.get(baseEntityRef), isSpecimenTypeDesignation);
135
        boolean hasExplicitBaseEntity = hasExplicitBaseEntity(baseEntityRef, typeDesignationWorkingSet);
133 136
        if(hasExplicitBaseEntity && !baseEntityRef.getLabel().isEmpty()){
134 137
            workingsetBuilder.add(TagEnum.specimenOrObservation, baseEntityRef.getLabel(), baseEntityRef);
135 138
        }
136
        TypeDesignationWorkingSet typeDesignationWorkingSet = orderedByTypesByBaseEntity.get(baseEntityRef);
137 139
        int typeStatusCount = 0;
138 140
        if (withBrackets && hasExplicitBaseEntity){
139 141
            workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_PARENTHESIS_LEFT);
140 142
        }
141 143
        for(TypeDesignationStatusBase<?> typeStatus : typeDesignationWorkingSet.keySet()) {
142 144
            typeStatusCount = buildTaggedTextForSingleTypeStatus(manager, workingsetBuilder,
143
                    typeDesignationWorkingSet, typeStatusCount, typeStatus, typeSetCount);
145
                    typeDesignationWorkingSet, typeStatusCount, typeStatus,
146
                    lastWsType, typeSetCount);
144 147
        }
145 148
        if (withBrackets && hasExplicitBaseEntity){
146 149
            workingsetBuilder.add(TagEnum.separator, TYPE_STATUS_PARENTHESIS_RIGHT);
......
155 158
     * Checks if the baseType is the same as the (only?) type in the type designation workingset.
156 159
     */
157 160
    private boolean hasExplicitBaseEntity(TypedEntityReference<?> baseEntityRef,
158
            TypeDesignationWorkingSet typeDesignationWorkingSet, boolean isSpecimenTypeDesignation) {
159
        if (!isSpecimenTypeDesignation){
161
            TypeDesignationWorkingSet typeDesignationWorkingSet) {
162
        if (!typeDesignationWorkingSet.isSpecimenWorkingSet()){
160 163
            return false;   //name type designations are not handled here
161 164
        }else{
162 165
            UUID baseUuid = baseEntityRef.getUuid();
......
171 174

  
172 175
    private int buildTaggedTextForSingleTypeStatus(TypeDesignationSetManager manager,
173 176
            TaggedTextBuilder workingsetBuilder, TypeDesignationWorkingSet typeDesignationWorkingSet,
174
            int typeStatusCount, TypeDesignationStatusBase<?> typeStatus, int typeSetCount) {
177
            int typeStatusCount, TypeDesignationStatusBase<?> typeStatus,
178
            TypeDesignationWorkingSetType lastWsType, int typeSetCount) {
175 179

  
176 180
        //starting separator
177 181
        if(typeStatusCount++ > 0){
......
179 183
        }
180 184

  
181 185
        boolean isPlural = typeDesignationWorkingSet.get(typeStatus).size() > 1;
186
        String label = null;
182 187
        if(typeStatus != TypeDesignationWorkingSet.NULL_STATUS){
183
            String label = typeStatus.getLabel() + (isPlural ? "s" : "");
188
            label = typeStatus.getLabel();
189
        }else if (typeDesignationWorkingSet.getWorkingsetType() != lastWsType
190
                && (workingsetBuilder.size() > 0 && typeSetCount > 0)){
191
            //only for the first name type (coming after a specimen type add the label (extremely rare case, if at all existing)
192
            if (typeDesignationWorkingSet.getWorkingsetType().isNameType()) {
193
                label = "nametype";
194
            }else if (typeDesignationWorkingSet.getWorkingsetType().isSpecimenType()) {
195
                label = "type";
196
            }
197
        }
198
        if (label != null){
199
            label = (isPlural ? label + "s" : label);
184 200
            if (workingsetBuilder.size() == 0){
185 201
                label = StringUtils.capitalize(label);
186 202
            }
187 203
            workingsetBuilder.add(TagEnum.label, label);
188 204
            workingsetBuilder.add(TagEnum.postSeparator, POST_STATUS_SEPARATOR);
189
        }else if (workingsetBuilder.size() > 0 && typeSetCount > 0){
190
            workingsetBuilder.add(TagEnum.label, (isPlural? "Nametypes:": "Nametype:"));
191 205
        }
192 206

  
193 207
        //designation + sources
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/name/TypeDesignationSetManager.java
17 17
import java.util.LinkedList;
18 18
import java.util.List;
19 19
import java.util.Map;
20
import java.util.Map.Entry;
20 21
import java.util.Optional;
21 22
import java.util.Set;
22 23
import java.util.UUID;
23 24

  
24 25
import eu.etaxonomy.cdm.api.service.exception.RegistrationValidationException;
26
import eu.etaxonomy.cdm.api.service.name.TypeDesignationWorkingSet.TypeDesignationWorkingSetType;
27
import eu.etaxonomy.cdm.compare.name.NullTypeDesignationStatus;
25 28
import eu.etaxonomy.cdm.compare.name.TypeDesignationStatusComparator;
26 29
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
27 30
import eu.etaxonomy.cdm.model.common.CdmBase;
......
73 76

  
74 77
    private TaxonName typifiedName;
75 78

  
79
    private Comparator<Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet>> entryComparator = new Comparator<Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet>>(){
80

  
81
        /**
82
          * Sorts the base entities (TypedEntityReference) in the following order:
83
          *
84
          * 1. FieldUnits
85
          * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
86
          * 3. NameType
87
          *
88
          * {@inheritDoc}
89
          */
90
         @Override
91
         public int compare(Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> o1, Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> o2) {
92

  
93
             TypeDesignationWorkingSet ws1 = o1.getValue();
94
             TypeDesignationWorkingSet ws2 = o2.getValue();
95

  
96
             if (ws1.getWorkingsetType() != ws2.getWorkingsetType()){
97
                 //first specimen types, then name types (very rare case anyway)
98
                 return ws1.getWorkingsetType() == TypeDesignationWorkingSetType.NAME_TYPE_DESIGNATION_WORKINGSET? 1:-1;
99
             }
100

  
101
             boolean hasStatus1 = !ws1.keySet().contains(null) && !ws1.keySet().contains(NullTypeDesignationStatus.SINGLETON());
102
             boolean hasStatus2 = !ws2.keySet().contains(null) && !ws2.keySet().contains(NullTypeDesignationStatus.SINGLETON());
103
             if (hasStatus1 != hasStatus2){
104
                 //first without status as it is difficult to distinguish a non status from a "same" status record if the first record has a status and second has no status
105
                 return hasStatus1? 1:-1;
106
             }
107

  
108
             //                boolean hasStatus1 = ws1.getTypeDesignations(); //.stream().filter(td -> td.getSt);
109

  
110
             Class<?> type1 = o1.getKey().getType();
111
             Class<?> type2 = o2.getKey().getType();
112

  
113
             if(!type1.equals(type2)) {
114
                 if(type1.equals(FieldUnit.class) || type2.equals(FieldUnit.class)){
115
                     // FieldUnits first
116
                     return type1.equals(FieldUnit.class) ? -1 : 1;
117
                 } else {
118
                     // name types last (in case of missing FieldUnit we expect the base type to be DerivedUnit which comes into the middle)
119
                     return type2.equals(TaxonName.class) || type2.equals(NameTypeDesignation.class) ? -1 : 1;
120
                 }
121
             } else {
122
                 return o1.getKey().getLabel().compareTo(o2.getKey().getLabel());
123
             }
124
         }};
125

  
76 126
    /**
77 127
     * Groups the EntityReferences for each of the TypeDesignations by the according TypeDesignationStatus.
78 128
     * The TypeDesignationStatusBase keys are already ordered by the term order defined in the vocabulary.
......
154 204
    protected void mapAndSort() {
155 205

  
156 206
        Map<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> byBaseEntityByTypeStatus = new HashMap<>();
157

  
158 207
        this.typeDesignations.values().forEach(td -> mapTypeDesignation(byBaseEntityByTypeStatus, td));
159 208
        orderedByTypesByBaseEntity = orderByTypeByBaseEntity(byBaseEntityByTypeStatus);
160 209
    }
......
257 306
            Map<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> stringsByTypeByBaseEntity){
258 307

  
259 308
       // order the FieldUnit TypeName keys
260
       List<TypedEntityReference<? extends VersionableEntity>> baseEntityKeyList = new LinkedList<>(stringsByTypeByBaseEntity.keySet());
261
       Collections.sort(baseEntityKeyList, new Comparator<TypedEntityReference<?>>(){
262

  
263
           /**
264
             * Sorts the base entities (TypedEntityReference) in the following order:
265
             *
266
             * 1. FieldUnits
267
             * 2. DerivedUnit (in case of missing FieldUnit we expect the base type to be DerivedUnit)
268
             * 3. NameType
269
             *
270
             * {@inheritDoc}
271
             */
272
            @Override
273
            public int compare(TypedEntityReference<?> o1, TypedEntityReference<?> o2) {
274

  
275
                Class<?> type1 = o1.getType();
276
                Class<?> type2 = o2.getType();
277

  
278
                if(!type1.equals(type2)) {
279
                    if(type1.equals(FieldUnit.class) || type2.equals(FieldUnit.class)){
280
                        // FieldUnits first
281
                        return type1.equals(FieldUnit.class) ? -1 : 1;
282
                    } else {
283
                        // name types last (in case of missing FieldUnit we expect the base type to be DerivedUnit which comes into the middle)
284
                        return type2.equals(TaxonName.class) || type2.equals(NameTypeDesignation.class) ? -1 : 1;
285
                    }
286
                } else {
287
                    return o1.getLabel().compareTo(o2.getLabel());
288
                }
289
            }}
290
       );
309
       Set<Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet>> entrySet = stringsByTypeByBaseEntity.entrySet();
310
       LinkedList<Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet>> baseEntityKeyList = new LinkedList<>(entrySet);
311
       Collections.sort(baseEntityKeyList, entryComparator);
291 312

  
292 313
       // new LinkedHashMap for the ordered FieldUnitOrTypeName keys
293 314
       LinkedHashMap<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> stringsOrderedbyBaseEntityOrderdByType
294 315
           = new LinkedHashMap<>(stringsByTypeByBaseEntity.size());
295 316

  
296
       for(TypedEntityReference<? extends VersionableEntity> baseEntityRef : baseEntityKeyList){
297

  
317
       for(Entry<TypedEntityReference<? extends VersionableEntity>, TypeDesignationWorkingSet> entry : baseEntityKeyList){
318
           TypedEntityReference<? extends VersionableEntity> baseEntityRef = entry.getKey();
298 319
           TypeDesignationWorkingSet typeDesignationWorkingSet = stringsByTypeByBaseEntity.get(baseEntityRef);
299 320
           // order the TypeDesignationStatusBase keys
300 321
            List<TypeDesignationStatusBase<?>> keyList = new LinkedList<>(typeDesignationWorkingSet.keySet());
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/name/TypeDesignationWorkingSet.java
104 104
        return baseEntityReference;
105 105
    }
106 106

  
107
    public boolean isSpecimenTypeDesigationWorkingSet() {
107
    public boolean isSpecimenWorkingSet() {
108
        return getWorkingsetType().isSpecimenType();
109
    }
110
    public boolean isNameWorkingSet() {
111
        return getWorkingsetType().isNameType();
112
    }
113

  
114
    private boolean isSpecimenTypeDesigationWorkingSet() {
108 115
        return SpecimenOrObservationBase.class.isAssignableFrom(baseEntity.getClass());
109 116
    }
110 117

  
......
115 122

  
116 123
    public enum TypeDesignationWorkingSetType {
117 124
        SPECIMEN_TYPE_DESIGNATION_WORKINGSET,
118
        NAME_TYPE_DESIGNATION_WORKINGSET,
125
        NAME_TYPE_DESIGNATION_WORKINGSET;
126
        boolean isSpecimenType(){return this == SPECIMEN_TYPE_DESIGNATION_WORKINGSET;}
127
        boolean isNameType(){return this == NAME_TYPE_DESIGNATION_WORKINGSET;}
119 128
    }
120 129

  
121 130
    @Override
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/name/TypeDesignationSetManagerTest.java
211 211
            assertEquals(
212 212
                    "Prionus L.\u202F\u2013\u202FTypes: Dreamland, near Kissingen, A.Kohlbecker 66211, 2017 (isotype: M);"
213 213
                    + " Testland, near Bughausen, A.Kohlbecker 81989, 2017 (holotype: OHA; isotypes: BER, KEW);"
214
                    + " Nametype: Prionus coriatius L."
214
                    + " nametype: Prionus coriatius L."
215 215
                    , result
216 216
                    );
217 217

  
......
250 250
            typeDesignationManager.addTypeDesigations(std_HT);
251 251

  
252 252
            assertEquals(
253
                    "Prionus L.\u202F\u2013\u202FTypes: Testland, near Bughausen, A.Kohlbecker 81989, 2017 (holotype: OHA); Nametype: Prionus coriatius L."
254
                    , typeDesignationManager.print(true, true, true)
253
                    "Prionus L.\u202F\u2013\u202FTypes: Testland, near Bughausen, A.Kohlbecker 81989, 2017 (holotype: OHA); nametype: Prionus coriatius L."
254
                    , typeDesignationManager.print(WITH_CITATION, WITH_TYPE_LABEL, true)
255 255
                    );
256 256

  
257 257
            DerivedUnit specimen = std_HT.getTypeSpecimen();
......
261 261
            specimen.setCollection(collection);
262 262

  
263 263
            assertEquals(
264
                    "Prionus L.\u202F\u2013\u202FTypes: Testland, near Bughausen, A.Kohlbecker 81989, 2017 (holotype: My collection); Nametype: Prionus coriatius L."
265
                    , typeDesignationManager.print(true, true, true)
264
                    "Prionus L.\u202F\u2013\u202FTypes: Testland, near Bughausen, A.Kohlbecker 81989, 2017 (holotype: My collection); nametype: Prionus coriatius L."
265
                    , typeDesignationManager.print(WITH_CITATION, WITH_TYPE_LABEL, true)
266 266
                    );
267 267
        }
268 268

  
......
349 349
            typeDesignationManager.addTypeDesigations(protectedDesignation);
350 350

  
351 351
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FNeotype: Mexico. Oaxaca: Coixtlahuaca, Tepelmeme Villa de Morelos, aproximadamente 1 km S del Río Santa Lucía, 1285 m, 27 March 1994, U. Guzmán Cruz 1065 (MEXU 280206) designated by The book of types: 55"
352
                    , typeDesignationManager.print(true, false, true)
352
                    , typeDesignationManager.print(WITH_CITATION, !WITH_TYPE_LABEL, true)
353
                    );
354
            protectedDesignation.setTypeStatus(null);
355
            typeDesignationManager.addTypeDesigations(protectedDesignation);
356
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FMexico. Oaxaca: Coixtlahuaca, Tepelmeme Villa de Morelos, aproximadamente 1 km S del Río Santa Lucía, 1285 m, 27 March 1994, U. Guzmán Cruz 1065 (MEXU 280206) designated by The book of types: 55"
357
                    , typeDesignationManager.print(WITH_CITATION, !WITH_TYPE_LABEL, true)
353 358
                    );
354 359

  
355 360
            DerivedUnit withoutFieldUnit = DerivedUnit.NewPreservedSpecimenInstance();
......
357 362
            withoutFieldUnit.setCollection(Collection.NewInstance("B", "Herbarium Berolinense"));
358 363
            SpecimenTypeDesignation withoutFieldUnitDesignation = typifiedName.addSpecimenTypeDesignation(withoutFieldUnit, SpecimenTypeDesignationStatus.HOLOTYPE(), null, null, null, false, false);
359 364
            typeDesignationManager.addTypeDesigations(withoutFieldUnitDesignation);
360
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FHolotype: B 280207; neotype: Mexico. Oaxaca: Coixtlahuaca, Tepelmeme Villa de Morelos, aproximadamente 1 km S del Río Santa Lucía, 1285 m, 27 March 1994, U. Guzmán Cruz 1065 (MEXU 280206) designated by The book of types: 55"
361
                    , typeDesignationManager.print(true, false, true)
365
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FMexico. Oaxaca: Coixtlahuaca, Tepelmeme Villa de Morelos, aproximadamente 1 km S del Río Santa Lucía, 1285 m, 27 March 1994, U. Guzmán Cruz 1065 (MEXU 280206) designated by The book of types: 55; holotype: B 280207"
366
                    , typeDesignationManager.print(WITH_CITATION, !WITH_TYPE_LABEL, true)
362 367
                    );
363 368
        }
369

  
370
        @Test
371
        public void test_withoutStatus(){
372
            //for another important test without status see also test_withoutFieldUnit()
373

  
374
            TaxonName typifiedName = TaxonNameFactory.NewBacterialInstance(Rank.SPECIES());
375
            typifiedName.setTitleCache("Prionus coriatius L.", true);
376
            TypeDesignationSetManager typeDesignationManager = new TypeDesignationSetManager(typifiedName);
377
            std_LT.setTypeStatus(null);
378
            typeDesignationManager.addTypeDesigations(std_LT);
379
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FType: Testland, near Bughausen, A.Kohlbecker 81989, 2017 (LEC designated by Decandolle & al. 1962)",
380
                    typeDesignationManager.print(WITH_CITATION, WITH_TYPE_LABEL, true));
381
            assertEquals("Prionus coriatius L.\u202F\u2013\u202FTestland, near Bughausen, A.Kohlbecker 81989, 2017 (LEC)",
382
                    typeDesignationManager.print(!WITH_CITATION, !WITH_TYPE_LABEL, true));
383
            assertEquals("Testland, near Bughausen, A.Kohlbecker 81989, 2017 (LEC)",
384
                    typeDesignationManager.print(!WITH_CITATION, !WITH_TYPE_LABEL, false));
385

  
386
            //name types
387
            typifiedName = TaxonNameFactory.NewBacterialInstance(Rank.GENUS());
388
            typifiedName.setTitleCache("Prionus L.", true);
389
            typeDesignationManager = new TypeDesignationSetManager(typifiedName);
390
            typeDesignationManager.addTypeDesigations(ntd_LT);
391
//            ntd_LT.addPrimaryTaxonomicSource(inRef, "66");
392
            assertEquals("Prionus L.\u202F\u2013\u202FLectotype: Prionus arealus L. designated by Decandolle & al. 1962",
393
                    typeDesignationManager.print(WITH_CITATION, !WITH_TYPE_LABEL, true));
394
            assertEquals("Prionus L.\u202F\u2013\u202FLectotype: Prionus arealus L.",
395
                    typeDesignationManager.print(!WITH_CITATION, !WITH_TYPE_LABEL, true));
396
        }
364 397
}

Also available in: Unified diff

Add picture from clipboard (Maximum size: 40 MB)