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
 * @created 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 term, String label, String labelAbbrev){
159
        return new NamedArea(term, label, labelAbbrev);
160
    }
161

    
162
//**************************** VARIABLES *******************************/
163

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

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

    
172
    private Point pointApproximation;
173

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

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

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

    
184

    
185
//********************************** Constructor *******************************************************************/
186

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

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

    
197
//********************************* GETTER /SETTER *********************************************/
198

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
305
        return (NamedArea) area;
306
    }
307

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

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

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

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

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

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

    
345
        String levelString = csvLine.get(6);
346

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

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

    
363
	@Override
364
	protected int partOfCsvLineIndex(){
365
		return 7;
366
	}
367

    
368

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

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

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

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

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

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

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

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

    
452
	public static final NamedArea EUROPE(){
453
		return getContinentByUuid(uuidEurope);
454
	}
455

    
456
	public static final NamedArea AFRICA(){
457
		return getContinentByUuid(uuidAfrica);
458
	}
459

    
460
	public static final NamedArea ASIA_TEMPERATE(){
461
		return getContinentByUuid(uuidAsiaTemperate);
462
	}
463

    
464
	public static final NamedArea ASIA_TROPICAL(){
465
		return getContinentByUuid(uuidAsiaTropical);
466
	}
467

    
468
	public static final NamedArea NORTH_AMERICA(){
469
		return getContinentByUuid(uuidNAmerica);
470
	}
471

    
472
	public static final NamedArea ANTARCTICA(){
473
		return getContinentByUuid(uuidAntarctica);
474
	}
475

    
476
	public static final NamedArea SOUTH_AMERICA(){
477
		return getContinentByUuid(uuidSAmerica);
478
	}
479

    
480
	public static final NamedArea AUSTRALASIA(){
481
		return getContinentByUuid(uuidAustralasia);
482
	}
483

    
484
	public static final NamedArea PACIFIC(){
485
		return getContinentByUuid(uuidPacific);
486
	}
487

    
488

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

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

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

    
510
	}
511

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

    
551
    private static void initTdwgMaps(){
552
    	tdwglabelMap = new HashMap<String, UUID>();
553
    	tdwgAbbrevMap = new HashMap<String, UUID>();
554
    }
555

    
556

    
557

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

    
574
// ************** Hierarchie List ****************************
575

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

    
598

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

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

    
609
        }
610

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

    
620
///****************** toString ***********************************************/
621

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

    
647
                }
648
                return result;
649
            }
650
        }
651

    
652
    }
653

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

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

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

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

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

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

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

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

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

    
749
    }
750

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

    
763
// ******************* toString **********************************/
764

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

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

    
779
        return result;
780
    }
781

    
782

    
783

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

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

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

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

    
845
    //*********************************** CLONE *****************************************/
846

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

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

    
863
    }
864

    
865

    
866
}
(2-2/8)