Project

General

Profile

Download (28.7 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
package eu.etaxonomy.cdm.model.location;
10

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

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

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

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

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

    
87
    private static final long serialVersionUID = 6248434369557403036L;
88
    private static final Logger logger = Logger.getLogger(NamedArea.class);
89

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

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

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

    
116
    private static Map<String, UUID> tdwgAbbrevMap = null;
117
    private static Map<String, UUID> tdwgLabelMap = null;
118

    
119
    private static Map<UUID, NamedArea> tdwgTermMap = null;
120
    private static Map<UUID, NamedArea> continentMap = null;
121
    private static Map<UUID, NamedArea> waterbodyMap = null;
122

    
123
    private static Map<UUID, NamedArea> termMap = null;
124

    
125
	public static final NamedArea ARCTICOCEAN () { return waterbodyMap.get(uuidArcticOcean );}
126
	public static final NamedArea ATLANTICOCEAN () { return waterbodyMap.get(uuidAtlanticOcean );}
127
	public static final NamedArea PACIFICOCEAN () { return waterbodyMap.get(uuidPacificOcean );}
128
	public static final NamedArea INDIANOCEAN () { return waterbodyMap.get(uuidIndianOcean );}
129
	public static final NamedArea SOUTHERNOCEAN () { return waterbodyMap.get(uuidSouthernOcean );}
130
	public static final NamedArea MEDITERRANEANSEA () { return waterbodyMap.get(uuidMediterraneanSea );}
131
	public static final NamedArea BLACKSEA () { return waterbodyMap.get(uuidBlackSea );}
132
	public static final NamedArea CASPIANSEA () { return waterbodyMap.get(uuidCaspianSea );}
133
	public static final NamedArea REDSEA () { return waterbodyMap.get(uuidRedSea );}
134
	public static final NamedArea PERSIANGULF () { return waterbodyMap.get(uuidPersianGulf );}
135

    
136

    
137
//************************* FACTORY METHODS ****************************************/
138

    
139
    /**
140
     * Factory method
141
     * @return
142
     */
143
    public static NamedArea NewInstance(){
144
        return new NamedArea();
145
    }
146

    
147
    /**
148
     * Factory method
149
     * @return
150
     */
151
    public static NamedArea NewInstance(String description, String label, String labelAbbrev){
152
        return new NamedArea(description, label, labelAbbrev);
153
    }
154

    
155
//**************************** VARIABLES *******************************/
156

    
157
    //description of time valid context of this area. e.g. year range
158
    private TimePeriod validPeriod = TimePeriod.NewInstance();
159

    
160
    //Binary shape definition for user's defined area as polygon
161
    @ManyToOne(fetch = FetchType.LAZY)
162
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
163
    private Media shape;
164

    
165
    private Point pointApproximation;
166

    
167
    @ManyToMany(fetch = FetchType.LAZY)
168
    @JoinTable(name="DefinedTermBase_Country")
169
    private final Set<Country> countries = new HashSet<>();
170

    
171
    @ManyToOne(fetch = FetchType.LAZY)
172
    private NamedAreaType type;
173

    
174
    @ManyToOne(fetch = FetchType.LAZY)
175
    private NamedAreaLevel level;
176

    
177

    
178
//********************************** Constructor *******************************************************************/
179

    
180
  	//for hibernate use only
181
  	@Deprecated
182
  	protected NamedArea() {
183
    	super(TermType.NamedArea);
184
    }
185

    
186
    protected NamedArea(String description, String label, String labelAbbrev) {
187
        super(TermType.NamedArea, description, label, labelAbbrev);
188
    }
189

    
190
//********************************* GETTER /SETTER *********************************************/
191

    
192
    @XmlElement(name = "NamedAreaType")
193
    @XmlIDREF
194
    @XmlSchemaType(name = "IDREF")
195
    public NamedAreaType getType(){
196
        return this.type;
197
    }
198

    
199
    public void setType(NamedAreaType type){
200
        this.type = type;
201
    }
202

    
203
    @XmlElement(name = "NamedAreaLevel")
204
    @XmlIDREF
205
    @XmlSchemaType(name = "IDREF")
206
    public NamedAreaLevel getLevel(){
207
        return this.level;
208
    }
209

    
210
    public void setLevel(NamedAreaLevel level){
211
        this.level = level;
212
    }
213

    
214
    @XmlElement(name = "ValidPeriod")
215
    public TimePeriod getValidPeriod(){
216
        return this.validPeriod;
217
    }
218

    
219
    public void setValidPeriod(TimePeriod validPeriod){
220
        this.validPeriod = validPeriod;
221
    }
222

    
223
    @XmlElement(name = "Shape")
224
    @XmlIDREF
225
    @XmlSchemaType(name = "IDREF")
226
    public Media getShape(){
227
        return this.shape;
228
    }
229
    public void setShape(Media shape){
230
        this.shape = shape;
231
    }
232

    
233
    @XmlElementWrapper(name = "Countries")
234
    @XmlElement(name = "Country")
235
    @XmlIDREF
236
    @XmlSchemaType(name = "IDREF")
237
    public Set<Country> getCountries() {
238
        return countries;
239
    }
240

    
241
    public void addCountry(Country country) {
242
        this.countries.add(country);
243
    }
244

    
245
    public void removeCountry(Country country) {
246
        this.countries.remove(country);
247
    }
248

    
249
    @XmlElement(name = "PointApproximation")
250
    public Point getPointApproximation() {
251
        return pointApproximation;
252
    }
253
    public void setPointApproximation(Point pointApproximation) {
254
        this.pointApproximation = pointApproximation;
255
    }
256

    
257
    @Override
258
    @XmlElement(name = "KindOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
259
    @XmlIDREF
260
    @XmlSchemaType(name = "IDREF")
261
    public NamedArea getKindOf(){
262
        return super.getKindOf();
263
    }
264

    
265
    @Override
266
    public void setKindOf(NamedArea kindOf){
267
        super.setKindOf(kindOf);
268
    }
269

    
270
    @XmlElement(name = "PartOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
271
    @XmlIDREF
272
    @XmlSchemaType(name = "IDREF")
273
    @Override
274
    public NamedArea getPartOf(){
275
        return super.getPartOf();
276
    }
277

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

    
294
        if(!(area instanceof NamedArea)){
295
            area = HibernateProxyHelper.deproxy(area, NamedArea.class);
296
        }
297

    
298
        return (NamedArea) area;
299
    }
300

    
301
    @Override
302
    public void setPartOf(NamedArea partOf){
303
        this.partOf = partOf;
304
    }
305

    
306
    @Override
307
    @XmlElementWrapper(name = "Generalizations", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
308
    @XmlElement(name = "GeneralizationOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
309
    @XmlIDREF
310
    @XmlSchemaType(name = "IDREF")
311
    public Set<NamedArea> getGeneralizationOf(){
312
        return super.getGeneralizationOf();
313
    }
314

    
315
    @Override
316
    protected void setGeneralizationOf(Set<NamedArea> value){
317
        super.setGeneralizationOf(value);
318
    }
319

    
320
    @Override
321
    @XmlElementWrapper(name = "Includes", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
322
    @XmlElement(name = "Include", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
323
    @XmlIDREF
324
    @XmlSchemaType(name = "IDREF")
325
    public Set<NamedArea> getIncludes(){
326
        return super.getIncludes();
327
    }
328

    
329
    @Override
330
    protected void setIncludes(Set<NamedArea> includes) {
331
        super.setIncludes(includes);
332
    }
333

    
334
    @Override
335
    public NamedArea readCsvLine(Class<NamedArea> termClass, List<String> csvLine, TermType termType,
336
            Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
337
        NamedArea newInstance = super.readCsvLine(termClass, csvLine, termType, terms, abbrevAsId);
338

    
339
        String levelString = csvLine.get(6);
340

    
341
        if(levelString != null && levelString.length() != 0) {
342
            UUID levelUuid = UUID.fromString(levelString);
343
            NamedAreaLevel level = (NamedAreaLevel)terms.get(levelUuid);
344
            newInstance.setLevel(level);
345
        }
346

    
347
//        String partOfString = csvLine.get(7);
348
//
349
//        if(partOfString != null && partOfString.length() != 0) {
350
//            UUID partOfUuid = UUID.fromString(partOfString);
351
//            NamedArea partOf = (NamedArea)terms.get(partOfUuid);
352
//            partOf.addIncludes(newInstance);
353
//        }
354
        return newInstance;
355
    }
356

    
357
	@Override
358
	protected int partOfCsvLineIndex(){
359
		return 7;
360
	}
361

    
362
    @Override
363
    public void resetTerms(){
364
        termMap = null;
365
        tdwgAbbrevMap = null;
366
   		tdwgLabelMap = null;
367
   		tdwgTermMap = null;
368
   		continentMap = null;
369
   		waterbodyMap = null;
370
    }
371

    
372
	@Deprecated //preliminary, will be removed in future
373
    protected static NamedArea getContinentByUuid(UUID uuid){
374
		if (continentMap == null){
375
			return null;
376
		}else{
377
			return continentMap.get(uuid);
378
		}
379
	}
380

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

    
390
    @Deprecated //preliminary, will be removed in future
391
    protected static NamedArea getTdwgTermByUuid(UUID uuid){
392
        if (tdwgTermMap == null){
393
            DefaultTermInitializer vocabularyStore = new DefaultTermInitializer();
394
            vocabularyStore.initialize();
395
        }
396
        return tdwgTermMap.get(uuid);
397
    }
398

    
399
    @Deprecated //preliminary, will be removed in future
400
    public static NamedArea getAreaByTdwgAbbreviation(String tdwgAbbreviation){
401
        if (tdwgAbbrevMap == null){
402
        	initTdwgMaps();
403
        }
404
        UUID uuid = tdwgAbbrevMap.get(tdwgAbbreviation);
405
        if (uuid == null){
406
            logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgAbbreviation));
407
            return null;
408
        }
409
        return NamedArea.getTdwgTermByUuid(uuid);
410
    }
411

    
412
    @Deprecated //preliminary, will be removed in future
413
    public static NamedArea getAreaByTdwgLabel(String tdwgLabel){
414
        if (tdwgLabelMap == null){
415
            initTdwgMaps();
416
        }
417
        tdwgLabel = tdwgLabel.toLowerCase();
418
        UUID uuid = tdwgLabelMap.get(tdwgLabel);
419
        if (uuid == null){
420
            logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgLabel));
421
            return null;
422
        }
423
        return NamedArea.getTdwgTermByUuid(uuid);
424
    }
425

    
426
    @Deprecated //preliminary, will be removed in future
427
    public static boolean isTdwgAreaLabel(String label){
428
        label = (label == null? null : label.toLowerCase());
429
        if (tdwgLabelMap.containsKey(label)){
430
            return true;
431
        }else{
432
            return false;
433
        }
434
    }
435

    
436
    @Deprecated //preliminary, will be removed in future
437
    public static boolean isTdwgAreaAbbreviation(String abbrev){
438
        if (tdwgAbbrevMap.containsKey(abbrev)){
439
            return true;
440
        }else{
441
            return false;
442
        }
443
    }
444

    
445
	public static final NamedArea EUROPE(){
446
		return getContinentByUuid(uuidEurope);
447
	}
448

    
449
	public static final NamedArea AFRICA(){
450
		return getContinentByUuid(uuidAfrica);
451
	}
452

    
453
	public static final NamedArea ASIA_TEMPERATE(){
454
		return getContinentByUuid(uuidAsiaTemperate);
455
	}
456

    
457
	public static final NamedArea ASIA_TROPICAL(){
458
		return getContinentByUuid(uuidAsiaTropical);
459
	}
460

    
461
	public static final NamedArea NORTH_AMERICA(){
462
		return getContinentByUuid(uuidNAmerica);
463
	}
464

    
465
	public static final NamedArea ANTARCTICA(){
466
		return getContinentByUuid(uuidAntarctica);
467
	}
468

    
469
	public static final NamedArea SOUTH_AMERICA(){
470
		return getContinentByUuid(uuidSAmerica);
471
	}
472

    
473
	public static final NamedArea AUSTRALASIA(){
474
		return getContinentByUuid(uuidAustralasia);
475
	}
476

    
477
	public static final NamedArea PACIFIC(){
478
		return getContinentByUuid(uuidPacific);
479
	}
480

    
481
	protected void setDefaultContinentTerms(TermVocabulary<NamedArea> termVocabulary) {
482
		continentMap = new HashMap<>();
483
		for (NamedArea term : termVocabulary.getTerms()){
484
			continentMap.put(term.getUuid(), term);  //TODO casting
485
		}
486
	}
487

    
488
	protected void setDefaultWaterbodyTerms(TermVocabulary<NamedArea> termVocabulary) {
489
		waterbodyMap = new HashMap<>();
490
		for (NamedArea term : termVocabulary.getTerms()){
491
			waterbodyMap.put(term.getUuid(), term);  //TODO casting
492
		}
493
	}
494

    
495
	protected void setTdwgDefaultTerms(TermVocabulary<NamedArea> tdwgTermVocabulary) {
496
        tdwgTermMap = new HashMap<>();
497
        for (NamedArea term : tdwgTermVocabulary.getTerms()){
498
            tdwgTermMap.put(term.getUuid(), term);  //TODO casting
499
            addTdwgArea(term);
500
        }
501
	}
502

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

    
542
    private static void initTdwgMaps(){
543
    	tdwgLabelMap = new HashMap<>();
544
    	tdwgAbbrevMap = new HashMap<>();
545
    }
546

    
547
    @Override
548
    protected void setDefaultTerms(TermVocabulary<NamedArea> termVocabulary) {
549
        if (termVocabulary.getUuid().equals(NamedArea.uuidTdwgAreaVocabulary)){
550
        	this.setTdwgDefaultTerms(termVocabulary);
551
        }else if (termVocabulary.getUuid().equals(NamedArea.uuidContinentVocabulary)){
552
        	this.setDefaultContinentTerms(termVocabulary);
553
        }else if (termVocabulary.getUuid().equals(NamedArea.uuidWaterbodyVocabulary)){
554
        	this.setDefaultWaterbodyTerms(termVocabulary);
555
        }else{
556
	    	termMap = new HashMap<>();
557
	        for (NamedArea term : termVocabulary.getTerms()){
558
	            termMap.put(term.getUuid(), term);
559
	        }
560
        }
561
    }
562

    
563
// ************** Hierarchie List ****************************
564

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

    
587
    public static class LevelNode {
588
        NamedAreaLevel level;
589
        List<NamedAreaNode> areaList = new ArrayList<>();
590

    
591
        public NamedAreaNode add(NamedArea area) {
592
            NamedAreaNode node = new NamedAreaNode();
593
            node.area = area;
594
            areaList.add(node);
595
            return node;
596
        }
597

    
598
        public NamedAreaNode getNamedAreaNode(NamedArea area) {
599
            for (NamedAreaNode node : areaList) {
600
                if (node.area.equals(area)) {
601
                    return node;
602
                }
603
            }
604
            return null;
605
        }
606

    
607
///****************** toString ***********************************************/
608

    
609
        @Override
610
        public String toString() {
611
            return toString(false, 0);
612
        }
613
        public String toString(boolean recursive, int identation) {
614
            String result = level == null? "" :level.getTitleCache();
615
            if (recursive == false){
616
                return result;
617
            }else{
618
                int areaSize = this.areaList.size();
619
                if (areaSize > 0){
620
                    result = "\n" + StringUtils.leftPad("", identation) + result  + "[";
621
                }
622
                boolean isFirst = true;
623
                for (NamedAreaNode level: this.areaList){
624
                    if (isFirst){
625
                        isFirst = false;
626
                    }else{
627
                        result += ",";
628
                    }
629
                    result += level.toString(recursive, identation+1);
630
                }
631
                if (areaSize > 0){
632
                    result += "]";
633
                }
634
                return result;
635
            }
636
        }
637
    }
638

    
639
    public static class NamedAreaNode {
640
        NamedArea area;
641
        List<LevelNode> levelList = new ArrayList<>();
642

    
643
        public LevelNode getLevelNode(NamedAreaLevel level) {
644
            for (LevelNode node : levelList) {
645
                if (node.level != null &&  node.level.equals(level)) {
646
                    return node;
647
                }
648
            }
649
            return null;
650
        }
651

    
652
        public List<NamedAreaNode> getList(NamedAreaLevel level) {
653
            LevelNode node = getLevelNode(level);
654
            if (node == null) {
655
                return new ArrayList<>();
656
            } else {
657
                return node.areaList;
658
            }
659
        }
660

    
661
        public boolean contains(NamedAreaLevel level) {
662
            if (getList(level).size() > 0) {
663
                return true;
664
            } else {
665
                return false;
666
            }
667
        }
668

    
669
        public LevelNode add(NamedAreaLevel level) {
670
            LevelNode node = new LevelNode();
671
            node.level = level;
672
            levelList.add(node);
673
            return node;
674
        }
675

    
676
        @Override
677
        public String toString() {
678
            return toString(false, 0);
679
        }
680

    
681
        public String toString(boolean recursive, int identation) {
682
            String result = "";
683
            if (area != null) {
684
                result = area.getTitleCache();
685
            }
686
            if (recursive){
687
                int levelSize = this.levelList.size();
688
                if (levelSize > 0){
689
                    result = "\n" + StringUtils.leftPad("", identation) + result  + "[";
690
                }
691
                boolean isFirst = true;
692
                for (LevelNode level: this.levelList){
693
                    if (isFirst){
694
                        isFirst = false;
695
                    }else{
696
                        result += ";";
697
                    }
698
                    result += level.toString(recursive, identation+1);
699
                }
700
                if (levelSize > 0){
701
                    result += "]";
702

    
703
                }
704
                return result;
705
            }else{
706
                int levelSize = this.levelList.size();
707
                return result + "[" + levelSize + " sublevel(s)]";
708
            }
709
        }
710
    }
711

    
712
    private static void mergeIntoResult(NamedAreaNode root, List<NamedArea> areaHierarchie) {
713
        if (areaHierarchie.isEmpty()) {
714
            return;
715
        }
716
        NamedArea highestArea = areaHierarchie.get(0);
717
        NamedAreaLevel level = highestArea.getLevel();
718
        NamedAreaNode namedAreaNode;
719
        if (! root.contains(level)) {
720
            LevelNode node = root.add(level);
721
            namedAreaNode = node.add(highestArea);
722
            //NEW
723
//			root.area = highestArea;
724
        } else {
725
            LevelNode levelNode = root.getLevelNode(level);
726
            namedAreaNode = levelNode.getNamedAreaNode(highestArea);
727
            if (namedAreaNode == null) {
728
                namedAreaNode = levelNode.add(highestArea);
729
            }
730
        }
731
        List<NamedArea> newList = areaHierarchie.subList(1, areaHierarchie.size());
732
        mergeIntoResult(namedAreaNode, newList);
733
    }
734

    
735
    @Transient
736
    public List<NamedArea> getAllLevelList() {
737
        List<NamedArea> result = new ArrayList<>();
738
        NamedArea copyArea = this;
739
        result.add(copyArea);
740
        while (copyArea.getPartOf() != null) {
741
            copyArea = copyArea.getPartOf();
742
            result.add(0, copyArea);
743
        }
744
        return result;
745
    }
746

    
747
// ******************* toString **********************************/
748

    
749
    @Override
750
    public String toString(){
751
        String result, label, IdInVocabulary, level = "";
752

    
753
        if (this.level != null){
754
            level = this.level.getLabel();
755
        }else{
756
            level = "no level";
757
        }
758
        label = this.getLabel();
759
        IdInVocabulary = getIdInVocabulary();
760
        IdInVocabulary = IdInVocabulary != null ? '<' + IdInVocabulary + '>' : "";
761
        result = "[" + level + ", " +  IdInVocabulary + label + "]";
762

    
763
        return result;
764
    }
765

    
766
    /**
767
     * Returns the label of the named area together with the area level label and the abbreviated label.
768
     * This is kind of a formatter method which may be moved to a better place in future.
769
     * @param namedArea the area
770
     * @param language the preferred language
771
     * @return null if namedArea == null, the labelWithLevel otherwise
772
     */
773
    public static String labelWithLevel(NamedArea namedArea, Language language) {
774
        if (namedArea == null){
775
            return null;
776
        }
777
        NamedArea area = CdmBase.deproxy(namedArea, NamedArea.class);
778

    
779
        StringBuilder title = new StringBuilder();
780
        Representation representation = area.getPreferredRepresentation(language);
781
        if (representation != null){
782
            String areaString = getPreferredAreaLabel(namedArea, representation);
783

    
784
            title.append(areaString);
785
        }else if (area.isProtectedTitleCache()){
786
        	title.append(area.getTitleCache());
787
        }else if (isNotBlank(area.getIdInVocabulary())){
788
        	title.append(area.getIdInVocabulary());
789
        }
790
        if (area.getLevel() == null){
791
        	title.append(" - ");
792
        	title.append(area.getClass().getSimpleName());
793
        }else{
794
        	title.append(" - ");
795
        	Representation levelRepresentation = area.getLevel().getPreferredRepresentation(language);
796
        	String levelString = getPreferredAreaLabel(area.getLevel(), levelRepresentation);
797
        	title.append(levelString);
798
        }
799
        return title.toString();
800
    }
801

    
802
    private static String getPreferredAreaLabel(DefinedTermBase<?> definedTerm, Representation representation) {
803
        String areaString = null;
804
        if (representation != null){
805
            areaString = representation.getLabel();
806
            if (isBlank(areaString)){
807
                areaString = representation.getAbbreviatedLabel();
808
            }
809
            if (isBlank(areaString)){
810
                areaString = representation.getText();
811
            }
812
        }
813
        if (isBlank(areaString)){
814
            areaString = definedTerm == null ? null : definedTerm.getTitleCache();
815
        }
816
        if (isBlank(areaString)){
817
            areaString = "no title";
818
        }
819
        return areaString;
820
    }
821

    
822
    //*********************************** CLONE *****************************************/
823

    
824
    /**
825
     * Clones <i>this</i> NamedArea. This is a shortcut that enables to create
826
     * a new instance that differs only slightly from <i>this</i> NamedArea by
827
     * modifying only some of the attributes.
828
     *
829
     * @see eu.etaxonomy.cdm.model.term.OrderedTermBase#clone()
830
     * @see java.lang.Object#clone()
831
     */
832
    @Override
833
    public NamedArea clone() {
834
        NamedArea result = (NamedArea)super.clone();
835

    
836
        //no changes to level, pointApproximation, shape, type, validPeriod and countries
837
        return result;
838
    }
839
}
(2-2/8)