Project

General

Profile

Download (29 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.model.location;
11

    
12

    
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.UUID;
20

    
21
import javax.persistence.Entity;
22
import javax.persistence.FetchType;
23
import javax.persistence.JoinTable;
24
import javax.persistence.ManyToMany;
25
import javax.persistence.ManyToOne;
26
import javax.persistence.Transient;
27
import javax.xml.bind.annotation.XmlAccessType;
28
import javax.xml.bind.annotation.XmlAccessorType;
29
import javax.xml.bind.annotation.XmlElement;
30
import javax.xml.bind.annotation.XmlElementWrapper;
31
import javax.xml.bind.annotation.XmlIDREF;
32
import javax.xml.bind.annotation.XmlRootElement;
33
import javax.xml.bind.annotation.XmlSchemaType;
34
import javax.xml.bind.annotation.XmlSeeAlso;
35
import javax.xml.bind.annotation.XmlType;
36

    
37
import org.apache.commons.lang.StringUtils;
38
import org.apache.log4j.Logger;
39
import org.hibernate.annotations.Cascade;
40
import org.hibernate.annotations.CascadeType;
41
import org.hibernate.envers.Audited;
42
import org.hibernate.search.annotations.ClassBridge;
43
import org.hibernate.search.annotations.Parameter;
44

    
45
import eu.etaxonomy.cdm.common.CdmUtils;
46
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
47
import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
48
import eu.etaxonomy.cdm.model.common.CdmBase;
49
import eu.etaxonomy.cdm.model.common.DefaultTermInitializer;
50
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
51
import eu.etaxonomy.cdm.model.common.Language;
52
import eu.etaxonomy.cdm.model.common.OrderedTermBase;
53
import eu.etaxonomy.cdm.model.common.Representation;
54
import eu.etaxonomy.cdm.model.common.TermType;
55
import eu.etaxonomy.cdm.model.common.TermVocabulary;
56
import eu.etaxonomy.cdm.model.common.TimePeriod;
57
import eu.etaxonomy.cdm.model.media.Media;
58

    
59
/**
60
 * @author m.doering
61
 * @since 08-Nov-2007 13:06:36
62
 */
63
@XmlAccessorType(XmlAccessType.PROPERTY)
64
@XmlType(name = "NamedArea", propOrder = {
65
    "kindOf",
66
    "generalizationOf",
67
    "partOf",
68
    "includes",
69
    "validPeriod",
70
    "shape",
71
    "pointApproximation",
72
    "countries",
73
    "type",
74
    "level"
75
})
76
@XmlRootElement(name = "NamedArea")
77
@XmlSeeAlso({
78
    Country.class
79
})
80
@Entity
81
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
82
//@Indexed(index = "eu.etaxonomy.cdm.model.common.DefinedTermBase")
83
@Audited
84
@ClassBridge(impl=DefinedTermBaseClassBridge.class, params={
85
    @Parameter(name="includeParentTerms", value="true")
86
})
87
public class NamedArea extends OrderedTermBase<NamedArea> implements Cloneable {
88
    private static final long serialVersionUID = 6248434369557403036L;
89
    private static final Logger logger = Logger.getLogger(NamedArea.class);
90

    
91

    
92
	//Continent UUIDs
93
    private static final UUID uuidEurope = UUID.fromString("3b69f979-408c-4080-b573-0ad78a315610");
94
	private static final UUID uuidAfrica = UUID.fromString("c204c529-d8d2-458f-b939-96f0ebd2cbe8");
95
	private static final UUID uuidAsiaTemperate = UUID.fromString("7f4f4f89-3b4c-475d-929f-144109bd8457");
96
	private static final UUID uuidAsiaTropical = UUID.fromString("f8039275-d2c0-4753-a1ab-0336642a1499");
97
	private static final UUID uuidNAmerica = UUID.fromString("81d8aca3-ddd7-4537-9f2b-5327c95b6e28");
98
	private static final UUID uuidSAmerica = UUID.fromString("12b861c9-c922-498c-8b1a-62afc26d19e3");
99
	private static final UUID uuidAustralasia = UUID.fromString("a2afdb9a-04a0-434c-9e75-d07dbeb86526");
100
	private static final UUID uuidPacific = UUID.fromString("c57adcff-5213-45f0-a5f0-97a9f5c0f1fe");
101
	private static final UUID uuidAntarctica = UUID.fromString("71fd9ab7-9b07-4eb6-8e54-c519aff56728");
102

    
103

    
104
    public static final UUID uuidTdwgAreaVocabulary = UUID.fromString("1fb40504-d1d7-44b0-9731-374fbe6cac77");
105
    public static final UUID uuidContinentVocabulary = UUID.fromString("e72cbcb6-58f8-4201-9774-15d0c6abc128");
106
    public static final UUID uuidWaterbodyVocabulary = UUID.fromString("35a62b25-f541-4f12-a7c7-17d90dec3e03");
107

    
108

    
109
	private static final UUID uuidArcticOcean = UUID.fromString("af4271e5-8897-4e6f-9db7-54ea4f28cfc0");
110
	private static final UUID uuidAtlanticOcean = UUID.fromString("77e79804-1b17-4c99-873b-933fe216e3da");
111
	private static final UUID uuidPacificOcean = UUID.fromString("3d68a327-104c-49d5-a2d8-c71c6600181b");
112
	private static final UUID uuidIndianOcean = UUID.fromString("ff744a37-5990-462c-9c20-1e85a9943851");
113
	private static final UUID uuidSouthernOcean = UUID.fromString("ef04f363-f67f-4a2c-8d98-110de4c5f654");
114
	private static final UUID uuidMediterraneanSea = UUID.fromString("8811a47e-29d6-4455-8f83-8916b78a692f");
115
	private static final UUID uuidBlackSea = UUID.fromString("4cb4bbae-9aab-426c-9025-e34f809165af");
116
	private static final UUID uuidCaspianSea = UUID.fromString("598fec0e-b93a-4947-a1f3-601e380797f7");
117
	private static final UUID uuidRedSea = UUID.fromString("ee69385e-6c80-405c-be6e-974e9fd1e297");
118
	private static final UUID uuidPersianGulf = UUID.fromString("8dc16e70-74b8-4143-95cf-a659a319a854");
119

    
120

    
121

    
122
    private static Map<String, UUID> tdwgAbbrevMap = null;
123
    private static Map<String, UUID> tdwgLabelMap = null;
124

    
125
    private static Map<UUID, NamedArea> tdwgTermMap = null;
126
    private static Map<UUID, NamedArea> continentMap = null;
127
    private static Map<UUID, NamedArea> waterbodyMap = null;
128

    
129

    
130
    private static Map<UUID, NamedArea> termMap = null;
131

    
132
	public static final NamedArea ARCTICOCEAN () { return waterbodyMap.get(uuidArcticOcean );}
133
	public static final NamedArea ATLANTICOCEAN () { return waterbodyMap.get(uuidAtlanticOcean );}
134
	public static final NamedArea PACIFICOCEAN () { return waterbodyMap.get(uuidPacificOcean );}
135
	public static final NamedArea INDIANOCEAN () { return waterbodyMap.get(uuidIndianOcean );}
136
	public static final NamedArea SOUTHERNOCEAN () { return waterbodyMap.get(uuidSouthernOcean );}
137
	public static final NamedArea MEDITERRANEANSEA () { return waterbodyMap.get(uuidMediterraneanSea );}
138
	public static final NamedArea BLACKSEA () { return waterbodyMap.get(uuidBlackSea );}
139
	public static final NamedArea CASPIANSEA () { return waterbodyMap.get(uuidCaspianSea );}
140
	public static final NamedArea REDSEA () { return waterbodyMap.get(uuidRedSea );}
141
	public static final NamedArea PERSIANGULF () { return waterbodyMap.get(uuidPersianGulf );}
142

    
143

    
144
//************************* FACTORY METHODS ****************************************/
145

    
146
    /**
147
     * Factory method
148
     * @return
149
     */
150
    public static NamedArea NewInstance(){
151
        return new NamedArea();
152
    }
153

    
154
    /**
155
     * Factory method
156
     * @return
157
     */
158
    public static NamedArea NewInstance(String description, String label, String labelAbbrev){
159
        return new NamedArea(description, label, labelAbbrev);
160
    }
161

    
162

    
163
//**************************** VARIABLES *******************************/
164

    
165
    //description of time valid context of this area. e.g. year range
166
    private TimePeriod validPeriod = TimePeriod.NewInstance();
167

    
168
    //Binary shape definition for user's defined area as polygon
169
    @ManyToOne(fetch = FetchType.LAZY)
170
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
171
    private Media shape;
172

    
173
    private Point pointApproximation;
174

    
175
    @ManyToMany(fetch = FetchType.LAZY)
176
    @JoinTable(name="DefinedTermBase_Country")
177
    private final Set<Country> countries = new HashSet<Country>();
178

    
179
    @ManyToOne(fetch = FetchType.LAZY)
180
    private NamedAreaType type;
181

    
182
    @ManyToOne(fetch = FetchType.LAZY)
183
    private NamedAreaLevel level;
184

    
185

    
186
//********************************** Constructor *******************************************************************/
187

    
188
  	//for hibernate use only
189
  	@Deprecated
190
  	protected NamedArea() {
191
    	super(TermType.NamedArea);
192
    }
193

    
194
    protected NamedArea(String description, String label, String labelAbbrev) {
195
        super(TermType.NamedArea, description, label, labelAbbrev);
196
    }
197

    
198
//********************************* GETTER /SETTER *********************************************/
199

    
200
    @XmlElement(name = "NamedAreaType")
201
    @XmlIDREF
202
    @XmlSchemaType(name = "IDREF")
203
    public NamedAreaType getType(){
204
        return this.type;
205
    }
206

    
207
    public void setType(NamedAreaType type){
208
        this.type = type;
209
    }
210

    
211
    @XmlElement(name = "NamedAreaLevel")
212
    @XmlIDREF
213
    @XmlSchemaType(name = "IDREF")
214
    public NamedAreaLevel getLevel(){
215
        return this.level;
216
    }
217

    
218
    public void setLevel(NamedAreaLevel level){
219
        this.level = level;
220
    }
221

    
222
    @XmlElement(name = "ValidPeriod")
223
    public TimePeriod getValidPeriod(){
224
        return this.validPeriod;
225
    }
226

    
227
    public void setValidPeriod(TimePeriod validPeriod){
228
        this.validPeriod = validPeriod;
229
    }
230

    
231
    @XmlElement(name = "Shape")
232
    @XmlIDREF
233
    @XmlSchemaType(name = "IDREF")
234
    public Media getShape(){
235
        return this.shape;
236
    }
237
    public void setShape(Media shape){
238
        this.shape = shape;
239
    }
240

    
241
    @XmlElementWrapper(name = "Countries")
242
    @XmlElement(name = "Country")
243
    @XmlIDREF
244
    @XmlSchemaType(name = "IDREF")
245
    public Set<Country> getCountries() {
246
        return countries;
247
    }
248

    
249
    public void addCountry(Country country) {
250
        this.countries.add(country);
251
    }
252

    
253
    public void removeCountry(Country country) {
254
        this.countries.remove(country);
255
    }
256

    
257
    @XmlElement(name = "PointApproximation")
258
    public Point getPointApproximation() {
259
        return pointApproximation;
260
    }
261
    public void setPointApproximation(Point pointApproximation) {
262
        this.pointApproximation = pointApproximation;
263
    }
264

    
265
    @Override
266
    @XmlElement(name = "KindOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
267
    @XmlIDREF
268
    @XmlSchemaType(name = "IDREF")
269
    public NamedArea getKindOf(){
270
        return super.getKindOf();
271
    }
272

    
273
    @Override
274
    public void setKindOf(NamedArea kindOf){
275
        super.setKindOf(kindOf);
276
    }
277

    
278
    @XmlElement(name = "PartOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
279
    @XmlIDREF
280
    @XmlSchemaType(name = "IDREF")
281
    @Override
282
    public NamedArea getPartOf(){
283
        return super.getPartOf();
284
    }
285

    
286
    /**
287
     * FIXME this method is a workaround for a casting problem in the getPartOf implementation
288
     *
289
     * the partOf instance variable is typically a proxy object of type DefinedTermBase, thus
290
     * does not coincide with the return value of NamedArea and a ClassCastException is thrown.
291
     *
292
     * It is not clear why this only occurs in the editor and not in the webservice where the same
293
     * method gets called and should lead to the same results.
294
     *
295
     * Seems to be a bigger problem although its origin is buggy behaviour of the javassist implementation.
296
     */
297
    @Deprecated
298
    @Transient
299
    public NamedArea getPartOfWorkaround(){
300
        Object area = super.getPartOf();
301

    
302
        if(!(area instanceof NamedArea)){
303
            area = HibernateProxyHelper.deproxy(area, NamedArea.class);
304
        }
305

    
306
        return (NamedArea) area;
307
    }
308

    
309
    @Override
310
    public void setPartOf(NamedArea partOf){
311
        this.partOf = partOf;
312
    }
313

    
314
    @Override
315
    @XmlElementWrapper(name = "Generalizations", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
316
    @XmlElement(name = "GeneralizationOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
317
    @XmlIDREF
318
    @XmlSchemaType(name = "IDREF")
319
    public Set<NamedArea> getGeneralizationOf(){
320
        return super.getGeneralizationOf();
321
    }
322

    
323
    @Override
324
    protected void setGeneralizationOf(Set<NamedArea> value){
325
        super.setGeneralizationOf(value);
326
    }
327

    
328
    @Override
329
    @XmlElementWrapper(name = "Includes", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
330
    @XmlElement(name = "Include", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
331
    @XmlIDREF
332
    @XmlSchemaType(name = "IDREF")
333
    public Set<NamedArea> getIncludes(){
334
        return super.getIncludes();
335
    }
336

    
337
    @Override
338
    protected void setIncludes(Set<NamedArea> includes) {
339
        super.setIncludes(includes);
340
    }
341

    
342
    @Override
343
    public NamedArea readCsvLine(Class<NamedArea> termClass, List<String> csvLine, TermType termType,
344
            Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
345
        NamedArea newInstance = super.readCsvLine(termClass, csvLine, termType, terms, abbrevAsId);
346

    
347
        String levelString = csvLine.get(6);
348

    
349
        if(levelString != null && levelString.length() != 0) {
350
            UUID levelUuid = UUID.fromString(levelString);
351
            NamedAreaLevel level = (NamedAreaLevel)terms.get(levelUuid);
352
            newInstance.setLevel(level);
353
        }
354

    
355
//        String partOfString = csvLine.get(7);
356
//
357
//        if(partOfString != null && partOfString.length() != 0) {
358
//            UUID partOfUuid = UUID.fromString(partOfString);
359
//            NamedArea partOf = (NamedArea)terms.get(partOfUuid);
360
//            partOf.addIncludes(newInstance);
361
//        }
362
        return newInstance;
363
    }
364

    
365
	@Override
366
	protected int partOfCsvLineIndex(){
367
		return 7;
368
	}
369

    
370

    
371
    @Override
372
    public void resetTerms(){
373
        termMap = null;
374
        tdwgAbbrevMap = null;
375
   		tdwgLabelMap = null;
376
   		tdwgTermMap = null;
377
   		continentMap = null;
378
   		waterbodyMap = null;
379
    }
380

    
381
	@Deprecated //preliminary, will be removed in future
382
    protected static NamedArea getContinentByUuid(UUID uuid){
383
		if (continentMap == null){
384
			return null;
385
		}else{
386
			return continentMap.get(uuid);
387
		}
388
	}
389

    
390
    @Deprecated //preliminary, will be removed in future
391
    protected static NamedArea getWaterbodyByUuid(UUID uuid){
392
		if (waterbodyMap == null){
393
			return null;
394
		}else{
395
			return waterbodyMap.get(uuid);
396
		}
397
    }
398

    
399
    @Deprecated //preliminary, will be removed in future
400
    protected static NamedArea getTdwgTermByUuid(UUID uuid){
401
        if (tdwgTermMap == null){
402
            DefaultTermInitializer vocabularyStore = new DefaultTermInitializer();
403
            vocabularyStore.initialize();
404
        }
405
        return tdwgTermMap.get(uuid);
406
    }
407

    
408
    @Deprecated //preliminary, will be removed in future
409
    public static NamedArea getAreaByTdwgAbbreviation(String tdwgAbbreviation){
410
        if (tdwgAbbrevMap == null){
411
        	initTdwgMaps();
412
        }
413
        UUID uuid = tdwgAbbrevMap.get(tdwgAbbreviation);
414
        if (uuid == null){
415
            logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgAbbreviation));
416
            return null;
417
        }
418
        return NamedArea.getTdwgTermByUuid(uuid);
419
    }
420

    
421
    @Deprecated //preliminary, will be removed in future
422
    public static NamedArea getAreaByTdwgLabel(String tdwgLabel){
423
        if (tdwgLabelMap == null){
424
            initTdwgMaps();
425
        }
426
        tdwgLabel = tdwgLabel.toLowerCase();
427
        UUID uuid = tdwgLabelMap.get(tdwgLabel);
428
        if (uuid == null){
429
            logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgLabel));
430
            return null;
431
        }
432
        return NamedArea.getTdwgTermByUuid(uuid);
433
    }
434

    
435
    @Deprecated //preliminary, will be removed in future
436
    public static boolean isTdwgAreaLabel(String label){
437
        label = (label == null? null : label.toLowerCase());
438
        if (tdwgLabelMap.containsKey(label)){
439
            return true;
440
        }else{
441
            return false;
442
        }
443
    }
444

    
445
    @Deprecated //preliminary, will be removed in future
446
    public static boolean isTdwgAreaAbbreviation(String abbrev){
447
        if (tdwgAbbrevMap.containsKey(abbrev)){
448
            return true;
449
        }else{
450
            return false;
451
        }
452
    }
453

    
454
	public static final NamedArea EUROPE(){
455
		return getContinentByUuid(uuidEurope);
456
	}
457

    
458
	public static final NamedArea AFRICA(){
459
		return getContinentByUuid(uuidAfrica);
460
	}
461

    
462
	public static final NamedArea ASIA_TEMPERATE(){
463
		return getContinentByUuid(uuidAsiaTemperate);
464
	}
465

    
466
	public static final NamedArea ASIA_TROPICAL(){
467
		return getContinentByUuid(uuidAsiaTropical);
468
	}
469

    
470
	public static final NamedArea NORTH_AMERICA(){
471
		return getContinentByUuid(uuidNAmerica);
472
	}
473

    
474
	public static final NamedArea ANTARCTICA(){
475
		return getContinentByUuid(uuidAntarctica);
476
	}
477

    
478
	public static final NamedArea SOUTH_AMERICA(){
479
		return getContinentByUuid(uuidSAmerica);
480
	}
481

    
482
	public static final NamedArea AUSTRALASIA(){
483
		return getContinentByUuid(uuidAustralasia);
484
	}
485

    
486
	public static final NamedArea PACIFIC(){
487
		return getContinentByUuid(uuidPacific);
488
	}
489

    
490

    
491
	protected void setDefaultContinentTerms(TermVocabulary<NamedArea> termVocabulary) {
492
		continentMap = new HashMap<>();
493
		for (NamedArea term : termVocabulary.getTerms()){
494
			continentMap.put(term.getUuid(), term);  //TODO casting
495
		}
496
	}
497

    
498
	protected void setDefaultWaterbodyTerms(TermVocabulary<NamedArea> termVocabulary) {
499
		waterbodyMap = new HashMap<>();
500
		for (NamedArea term : termVocabulary.getTerms()){
501
			waterbodyMap.put(term.getUuid(), term);  //TODO casting
502
		}
503
	}
504

    
505
	protected void setTdwgDefaultTerms(TermVocabulary<NamedArea> tdwgTermVocabulary) {
506
        tdwgTermMap = new HashMap<>();
507
        for (NamedArea term : tdwgTermVocabulary.getTerms()){
508
            tdwgTermMap.put(term.getUuid(), term);  //TODO casting
509
            addTdwgArea(term);
510
        }
511

    
512
	}
513

    
514
   protected static void addTdwgArea(NamedArea area){
515
        if (area == null){
516
            logger.warn("tdwg area is null");
517
            return;
518
        }
519
        Language lang = Language.DEFAULT();
520
        Representation representation = area.getRepresentation(lang);
521
        String tdwgAbbrevLabel = representation.getAbbreviatedLabel();
522
        String tdwgLabel = representation.getLabel().toLowerCase();
523
        if (tdwgAbbrevLabel == null){
524
            logger.warn("tdwgLabel = null");
525
            return;
526
        }
527
        //init map
528
        if (tdwgAbbrevMap == null){
529
        	tdwgAbbrevMap = new HashMap<>();
530
        }
531
        if (tdwgLabelMap == null){
532
        	tdwgLabelMap = new HashMap<>();
533
        }
534
        //add to map
535
        tdwgAbbrevMap.put(tdwgAbbrevLabel, area.getUuid());
536
        tdwgLabelMap.put(tdwgLabel, area.getUuid());
537
        //add type
538
        area.setType(NamedAreaType.ADMINISTRATION_AREA());
539
        //add level
540
        if (tdwgAbbrevLabel.trim().length()== 1){
541
            area.setLevel(NamedAreaLevel.TDWG_LEVEL1());
542
        }else if (tdwgAbbrevLabel.trim().length()== 2){
543
            area.setLevel(NamedAreaLevel.TDWG_LEVEL2());
544
        }else if (tdwgAbbrevLabel.trim().length()== 3){
545
            area.setLevel(NamedAreaLevel.TDWG_LEVEL3());
546
        }else if (tdwgAbbrevLabel.trim().length()== 6){
547
            area.setLevel(NamedAreaLevel.TDWG_LEVEL4());
548
        }else {
549
            logger.warn("Unknown TDWG Level " + tdwgAbbrevLabel + "! Unvalid string length (" +  tdwgAbbrevLabel.length() +")");
550
        }
551
    }
552

    
553
    private static void initTdwgMaps(){
554
    	tdwgLabelMap = new HashMap<>();
555
    	tdwgAbbrevMap = new HashMap<>();
556
    }
557

    
558

    
559

    
560
    @Override
561
    protected void setDefaultTerms(TermVocabulary<NamedArea> termVocabulary) {
562
        if (termVocabulary.getUuid().equals(NamedArea.uuidTdwgAreaVocabulary)){
563
        	this.setTdwgDefaultTerms(termVocabulary);
564
        }else if (termVocabulary.getUuid().equals(NamedArea.uuidContinentVocabulary)){
565
        	this.setDefaultContinentTerms(termVocabulary);
566
        }else if (termVocabulary.getUuid().equals(NamedArea.uuidWaterbodyVocabulary)){
567
        	this.setDefaultWaterbodyTerms(termVocabulary);
568
        }else{
569
	    	termMap = new HashMap<>();
570
	        for (NamedArea term : termVocabulary.getTerms()){
571
	            termMap.put(term.getUuid(), term);
572
	        }
573
        }
574
    }
575

    
576
// ************** Hierarchie List ****************************
577

    
578
    /**
579
     * This method returns a sorted tree structure which sorts areas by it's level and within the same level
580
     * alphabetically (TODO to be tested).
581
     * The structure returned is a tree with alternating nodes that represent an area and an areaLevel.
582
     * This way also areas that have children belonging to different levels can be handled.<BR>
583
     * The root node is always an empty area node which holds the list of top level areaLevels.
584
     * AreaLevels with no level defined are handled as if they have a separate level (level="null").
585
     *
586
     * There is a somehow similar implementation in {@link eu.etaxonomy.cdm.api.service.DistributionTree}
587
     *
588
     * @param areaList
589
     * @return
590
     */
591
    public static NamedAreaNode getHiearchieList(List<NamedArea> areaList){
592
        NamedAreaNode result = new NamedAreaNode();
593
        for (NamedArea area : areaList){
594
            List<NamedArea> areaHierarchie  = area.getAllLevelList();
595
            mergeIntoResult(result, areaHierarchie);
596
        }
597
        return result;
598
    }
599

    
600

    
601
    public static class LevelNode {
602
        NamedAreaLevel level;
603
        List<NamedAreaNode> areaList = new ArrayList<>();
604

    
605
        public NamedAreaNode add(NamedArea area) {
606
            NamedAreaNode node = new NamedAreaNode();
607
            node.area = area;
608
            areaList.add(node);
609
            return node;
610

    
611
        }
612

    
613
        public NamedAreaNode getNamedAreaNode(NamedArea area) {
614
            for (NamedAreaNode node : areaList) {
615
                if (node.area.equals(area)) {
616
                    return node;
617
                }
618
            }
619
            return null;
620
        }
621

    
622
///****************** toString ***********************************************/
623

    
624
        @Override
625
        public String toString() {
626
            return toString(false, 0);
627
        }
628
        public String toString(boolean recursive, int identation) {
629
            String result = level == null? "" :level.getTitleCache();
630
            if (recursive == false){
631
                return result;
632
            }else{
633
                int areaSize = this.areaList.size();
634
                if (areaSize > 0){
635
                    result = "\n" + StringUtils.leftPad("", identation) + result  + "[";
636
                }
637
                boolean isFirst = true;
638
                for (NamedAreaNode level: this.areaList){
639
                    if (isFirst){
640
                        isFirst = false;
641
                    }else{
642
                        result += ",";
643
                    }
644
                    result += level.toString(recursive, identation+1);
645
                }
646
                if (areaSize > 0){
647
                    result += "]";
648

    
649
                }
650
                return result;
651
            }
652
        }
653

    
654
    }
655

    
656
    public static class NamedAreaNode {
657
        NamedArea area;
658
        List<LevelNode> levelList = new ArrayList<>();
659

    
660
        public LevelNode getLevelNode(NamedAreaLevel level) {
661
            for (LevelNode node : levelList) {
662
                if (node.level != null &&  node.level.equals(level)) {
663
                    return node;
664
                }
665
            }
666
            return null;
667
        }
668

    
669
        public List<NamedAreaNode> getList(NamedAreaLevel level) {
670
            LevelNode node = getLevelNode(level);
671
            if (node == null) {
672
                return new ArrayList<>();
673
            } else {
674
                return node.areaList;
675
            }
676
        };
677

    
678
        public boolean contains(NamedAreaLevel level) {
679
            if (getList(level).size() > 0) {
680
                return true;
681
            } else {
682
                return false;
683
            }
684
        }
685

    
686
        public LevelNode add(NamedAreaLevel level) {
687
            LevelNode node = new LevelNode();
688
            node.level = level;
689
            levelList.add(node);
690
            return node;
691
        }
692

    
693
        @Override
694
        public String toString() {
695
            return toString(false, 0);
696
        }
697

    
698
        public String toString(boolean recursive, int identation) {
699
            String result = "";
700
            if (area != null) {
701
                result = area.getTitleCache();
702
            }
703
            if (recursive){
704
                int levelSize = this.levelList.size();
705
                if (levelSize > 0){
706
                    result = "\n" + StringUtils.leftPad("", identation) + result  + "[";
707
                }
708
                boolean isFirst = true;
709
                for (LevelNode level: this.levelList){
710
                    if (isFirst){
711
                        isFirst = false;
712
                    }else{
713
                        result += ";";
714
                    }
715
                    result += level.toString(recursive, identation+1);
716
                }
717
                if (levelSize > 0){
718
                    result += "]";
719

    
720
                }
721
                return result;
722
            }else{
723
                int levelSize = this.levelList.size();
724
                return result + "[" + levelSize + " sublevel(s)]";
725
            }
726
        }
727
    }
728

    
729
    private static void mergeIntoResult(NamedAreaNode root, List<NamedArea> areaHierarchie) {
730
        if (areaHierarchie.isEmpty()) {
731
            return;
732
        }
733
        NamedArea highestArea = areaHierarchie.get(0);
734
        NamedAreaLevel level = highestArea.getLevel();
735
        NamedAreaNode namedAreaNode;
736
        if (! root.contains(level)) {
737
            LevelNode node = root.add(level);
738
            namedAreaNode = node.add(highestArea);
739
            //NEW
740
//			root.area = highestArea;
741
        } else {
742
            LevelNode levelNode = root.getLevelNode(level);
743
            namedAreaNode = levelNode.getNamedAreaNode(highestArea);
744
            if (namedAreaNode == null) {
745
                namedAreaNode = levelNode.add(highestArea);
746
            }
747
        }
748
        List<NamedArea> newList = areaHierarchie.subList(1, areaHierarchie.size());
749
        mergeIntoResult(namedAreaNode, newList);
750

    
751
    }
752

    
753
    @Transient
754
    public List<NamedArea> getAllLevelList() {
755
        List<NamedArea> result = new ArrayList<>();
756
        NamedArea copyArea = this;
757
        result.add(copyArea);
758
        while (copyArea.getPartOf() != null) {
759
            copyArea = copyArea.getPartOf();
760
            result.add(0, copyArea);
761
        }
762
        return result;
763
    }
764

    
765
// ******************* toString **********************************/
766

    
767
    @Override
768
    public String toString(){
769
        String result, label, IdInVocabulary, level = "";
770

    
771
        if (this.level != null){
772
            level = this.level.getLabel();
773
        }else{
774
            level = "no level";
775
        }
776
        label = this.getLabel();
777
        IdInVocabulary = getIdInVocabulary();
778
        IdInVocabulary = IdInVocabulary != null ? '<' + IdInVocabulary + '>' : "";
779
        result = "[" + level + ", " +  IdInVocabulary + label + "]";
780

    
781
        return result;
782
    }
783

    
784

    
785

    
786
    /**
787
     * Returns the label of the named area together with the area level label and the abbreviated label.
788
     * This is kind of a formatter method which may be moved to a better place in future.
789
     * @param namedArea the area
790
     * @param language the preferred language
791
     * @return null if namedArea == null, the labelWithLevel otherwise
792
     */
793
    public static String labelWithLevel(NamedArea namedArea, Language language) {
794
        if (namedArea == null){
795
            return null;
796
        }
797
        NamedArea area = CdmBase.deproxy(namedArea, NamedArea.class);
798

    
799
        StringBuilder title = new StringBuilder();
800
        Representation representation = area.getPreferredRepresentation(language);
801
        if (representation != null){
802
            String areaString = getPreferredAreaLabel(namedArea, representation);
803

    
804
            title.append(areaString);
805
        }else if (area.isProtectedTitleCache()){
806
        	title.append(area.getTitleCache());
807
        }else if (StringUtils.isNotBlank(area.getIdInVocabulary())){
808
        	title.append(area.getIdInVocabulary());
809
        }
810
        if (area.getLevel() == null){
811
        	title.append(" - ");
812
        	title.append(area.getClass().getSimpleName());
813
        }else{
814
        	title.append(" - ");
815
        	Representation levelRepresentation = area.getLevel().getPreferredRepresentation(language);
816
        	String levelString = getPreferredAreaLabel(area.getLevel(), levelRepresentation);
817
        	title.append(levelString);
818
        }
819
        return title.toString();
820
    }
821

    
822
    /**
823
     * @param definedTerm
824
     * @param representation
825
     * @return
826
     */
827
    private static String getPreferredAreaLabel(DefinedTermBase<?> definedTerm, Representation representation) {
828
        String areaString = null;
829
        if (representation != null){
830
            areaString = representation.getLabel();
831
            if (StringUtils.isBlank(areaString)){
832
                areaString = representation.getAbbreviatedLabel();
833
            }
834
            if (StringUtils.isBlank(areaString)){
835
                areaString = representation.getText();
836
            }
837
        }
838
        if (StringUtils.isBlank(areaString)){
839
            areaString = definedTerm == null ? null : definedTerm.getTitleCache();
840
        }
841
        if (StringUtils.isBlank(areaString)){
842
            areaString = "no title";
843
        }
844
        return areaString;
845
    }
846

    
847
    //*********************************** CLONE *****************************************/
848

    
849
    /**
850
     * Clones <i>this</i> NamedArea. This is a shortcut that enables to create
851
     * a new instance that differs only slightly from <i>this</i> NamedArea by
852
     * modifying only some of the attributes.
853
     *
854
     * @see eu.etaxonomy.cdm.model.common.OrderedTermBase#clone()
855
     * @see java.lang.Object#clone()
856
     */
857
    @Override
858
    public Object clone() {
859
        NamedArea result;
860

    
861
            result = (NamedArea)super.clone();
862
            //no changes to level, pointApproximation, shape, type, validPeriod and countries
863
            return result;
864

    
865
    }
866

    
867

    
868
}
(2-2/8)