#4716 reduicing clutter in the lucene index and solving performance problems which...
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / location / NamedArea.java
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.JoinColumn;
24 import javax.persistence.JoinTable;
25 import javax.persistence.ManyToMany;
26 import javax.persistence.ManyToOne;
27 import javax.persistence.Transient;
28 import javax.xml.bind.annotation.XmlAccessType;
29 import javax.xml.bind.annotation.XmlAccessorType;
30 import javax.xml.bind.annotation.XmlElement;
31 import javax.xml.bind.annotation.XmlElementWrapper;
32 import javax.xml.bind.annotation.XmlIDREF;
33 import javax.xml.bind.annotation.XmlRootElement;
34 import javax.xml.bind.annotation.XmlSchemaType;
35 import javax.xml.bind.annotation.XmlSeeAlso;
36 import javax.xml.bind.annotation.XmlType;
37
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.log4j.Logger;
40 import org.hibernate.annotations.Cascade;
41 import org.hibernate.annotations.CascadeType;
42 import org.hibernate.envers.Audited;
43 import org.hibernate.search.annotations.ClassBridge;
44 import org.hibernate.search.annotations.Parameter;
45
46 import eu.etaxonomy.cdm.common.CdmUtils;
47 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
48 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
49 import eu.etaxonomy.cdm.model.common.CdmBase;
50 import eu.etaxonomy.cdm.model.common.DefaultTermInitializer;
51 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
52 import eu.etaxonomy.cdm.model.common.Language;
53 import eu.etaxonomy.cdm.model.common.OrderedTermBase;
54 import eu.etaxonomy.cdm.model.common.Representation;
55 import eu.etaxonomy.cdm.model.common.TermType;
56 import eu.etaxonomy.cdm.model.common.TermVocabulary;
57 import eu.etaxonomy.cdm.model.common.TimePeriod;
58 import eu.etaxonomy.cdm.model.media.Media;
59
60 /**
61 * @author m.doering
62 * @created 08-Nov-2007 13:06:36
63 */
64 @XmlAccessorType(XmlAccessType.PROPERTY)
65 @XmlType(name = "NamedArea", propOrder = {
66 "kindOf",
67 "generalizationOf",
68 "partOf",
69 "includes",
70 "validPeriod",
71 "shape",
72 "pointApproximation",
73 "countries",
74 "type",
75 "level"
76 })
77 @XmlRootElement(name = "NamedArea")
78 @XmlSeeAlso({
79 Country.class
80 })
81 @Entity
82 //@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
83 //@Indexed(index = "eu.etaxonomy.cdm.model.common.DefinedTermBase")
84 @Audited
85 @ClassBridge(impl=DefinedTermBaseClassBridge.class, params={
86 @Parameter(name="includeParentTerms", value="true")
87 })
88 public class NamedArea extends OrderedTermBase<NamedArea> implements Cloneable {
89 private static final long serialVersionUID = 6248434369557403036L;
90 private static final Logger logger = Logger.getLogger(NamedArea.class);
91
92
93 //Continent UUIDs
94 private static final UUID uuidEurope = UUID.fromString("3b69f979-408c-4080-b573-0ad78a315610");
95 private static final UUID uuidAfrica = UUID.fromString("c204c529-d8d2-458f-b939-96f0ebd2cbe8");
96 private static final UUID uuidAsiaTemperate = UUID.fromString("7f4f4f89-3b4c-475d-929f-144109bd8457");
97 private static final UUID uuidAsiaTropical = UUID.fromString("f8039275-d2c0-4753-a1ab-0336642a1499");
98 private static final UUID uuidNAmerica = UUID.fromString("81d8aca3-ddd7-4537-9f2b-5327c95b6e28");
99 private static final UUID uuidSAmerica = UUID.fromString("12b861c9-c922-498c-8b1a-62afc26d19e3");
100 private static final UUID uuidAustralasia = UUID.fromString("a2afdb9a-04a0-434c-9e75-d07dbeb86526");
101 private static final UUID uuidPacific = UUID.fromString("c57adcff-5213-45f0-a5f0-97a9f5c0f1fe");
102 private static final UUID uuidAntarctica = UUID.fromString("71fd9ab7-9b07-4eb6-8e54-c519aff56728");
103
104
105 public static final UUID uuidTdwgAreaVocabulary = UUID.fromString("1fb40504-d1d7-44b0-9731-374fbe6cac77");
106 public static final UUID uuidContinentVocabulary = UUID.fromString("e72cbcb6-58f8-4201-9774-15d0c6abc128");
107 public static final UUID uuidWaterbodyVocabulary = UUID.fromString("35a62b25-f541-4f12-a7c7-17d90dec3e03");
108
109
110 private static final UUID uuidArcticOcean = UUID.fromString("af4271e5-8897-4e6f-9db7-54ea4f28cfc0");
111 private static final UUID uuidAtlanticOcean = UUID.fromString("77e79804-1b17-4c99-873b-933fe216e3da");
112 private static final UUID uuidPacificOcean = UUID.fromString("3d68a327-104c-49d5-a2d8-c71c6600181b");
113 private static final UUID uuidIndianOcean = UUID.fromString("ff744a37-5990-462c-9c20-1e85a9943851");
114 private static final UUID uuidSouthernOcean = UUID.fromString("ef04f363-f67f-4a2c-8d98-110de4c5f654");
115 private static final UUID uuidMediterraneanSea = UUID.fromString("8811a47e-29d6-4455-8f83-8916b78a692f");
116 private static final UUID uuidBlackSea = UUID.fromString("4cb4bbae-9aab-426c-9025-e34f809165af");
117 private static final UUID uuidCaspianSea = UUID.fromString("598fec0e-b93a-4947-a1f3-601e380797f7");
118 private static final UUID uuidRedSea = UUID.fromString("ee69385e-6c80-405c-be6e-974e9fd1e297");
119 private static final UUID uuidPersianGulf = UUID.fromString("8dc16e70-74b8-4143-95cf-a659a319a854");
120
121
122
123 private static Map<String, UUID> tdwgAbbrevMap = null;
124 private static Map<String, UUID> tdwglabelMap = null;
125
126 private static Map<UUID, NamedArea> tdwgTermMap = null;
127 private static Map<UUID, NamedArea> continentMap = null;
128 private static Map<UUID, NamedArea> waterbodyMap = null;
129
130
131 private static Map<UUID, NamedArea> termMap = null;
132
133 public static final NamedArea ARCTICOCEAN () { return waterbodyMap.get(uuidArcticOcean );}
134 public static final NamedArea ATLANTICOCEAN () { return waterbodyMap.get(uuidAtlanticOcean );}
135 public static final NamedArea PACIFICOCEAN () { return waterbodyMap.get(uuidPacificOcean );}
136 public static final NamedArea INDIANOCEAN () { return waterbodyMap.get(uuidIndianOcean );}
137 public static final NamedArea SOUTHERNOCEAN () { return waterbodyMap.get(uuidSouthernOcean );}
138 public static final NamedArea MEDITERRANEANSEA () { return waterbodyMap.get(uuidMediterraneanSea );}
139 public static final NamedArea BLACKSEA () { return waterbodyMap.get(uuidBlackSea );}
140 public static final NamedArea CASPIANSEA () { return waterbodyMap.get(uuidCaspianSea );}
141 public static final NamedArea REDSEA () { return waterbodyMap.get(uuidRedSea );}
142 public static final NamedArea PERSIANGULF () { return waterbodyMap.get(uuidPersianGulf );}
143
144
145 //************************* FACTORY METHODS ****************************************/
146
147 /**
148 * Factory method
149 * @return
150 */
151 public static NamedArea NewInstance(){
152 return new NamedArea();
153 }
154
155 /**
156 * Factory method
157 * @return
158 */
159 public static NamedArea NewInstance(String term, String label, String labelAbbrev){
160 return new NamedArea(term, label, labelAbbrev);
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 //preliminary #5369
177 @JoinTable(
178 name="DefinedTermBase_Country",
179 joinColumns = @JoinColumn( name="DefinedTermBase_id")
180 )
181 private final Set<Country> countries = new HashSet<Country>();
182
183 @ManyToOne(fetch = FetchType.LAZY)
184 private NamedAreaType type;
185
186 @ManyToOne(fetch = FetchType.LAZY)
187 private NamedAreaLevel level;
188
189
190 //********************************** Constructor *******************************************************************/
191
192 //for hibernate use only
193 @Deprecated
194 protected NamedArea() {
195 super(TermType.NamedArea);
196 }
197
198 protected NamedArea(String term, String label, String labelAbbrev) {
199 super(TermType.NamedArea, term, label, labelAbbrev);
200 }
201
202 //********************************* GETTER /SETTER *********************************************/
203
204 @XmlElement(name = "NamedAreaType")
205 @XmlIDREF
206 @XmlSchemaType(name = "IDREF")
207 public NamedAreaType getType(){
208 return this.type;
209 }
210
211 public void setType(NamedAreaType type){
212 this.type = type;
213 }
214
215 @XmlElement(name = "NamedAreaLevel")
216 @XmlIDREF
217 @XmlSchemaType(name = "IDREF")
218 public NamedAreaLevel getLevel(){
219 return this.level;
220 }
221
222 public void setLevel(NamedAreaLevel level){
223 this.level = level;
224 }
225
226 @XmlElement(name = "ValidPeriod")
227 public TimePeriod getValidPeriod(){
228 return this.validPeriod;
229 }
230
231 public void setValidPeriod(TimePeriod validPeriod){
232 this.validPeriod = validPeriod;
233 }
234
235 @XmlElement(name = "Shape")
236 @XmlIDREF
237 @XmlSchemaType(name = "IDREF")
238 public Media getShape(){
239 return this.shape;
240 }
241 public void setShape(Media shape){
242 this.shape = shape;
243 }
244
245 @XmlElementWrapper(name = "Countries")
246 @XmlElement(name = "Country")
247 @XmlIDREF
248 @XmlSchemaType(name = "IDREF")
249 public Set<Country> getCountries() {
250 return countries;
251 }
252
253 public void addCountry(Country country) {
254 this.countries.add(country);
255 }
256
257 public void removeCountry(Country country) {
258 this.countries.remove(country);
259 }
260
261 @XmlElement(name = "PointApproximation")
262 public Point getPointApproximation() {
263 return pointApproximation;
264 }
265 public void setPointApproximation(Point pointApproximation) {
266 this.pointApproximation = pointApproximation;
267 }
268
269 @Override
270 @XmlElement(name = "KindOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
271 @XmlIDREF
272 @XmlSchemaType(name = "IDREF")
273 public NamedArea getKindOf(){
274 return super.getKindOf();
275 }
276
277 @Override
278 public void setKindOf(NamedArea kindOf){
279 super.setKindOf(kindOf);
280 }
281
282 @XmlElement(name = "PartOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
283 @XmlIDREF
284 @XmlSchemaType(name = "IDREF")
285 @Override
286 public NamedArea getPartOf(){
287 return super.getPartOf();
288 }
289
290 /**
291 * FIXME this method is a workaround for a casting problem in the getPartOf implementation
292 *
293 * the partOf instance variable is typically a proxy object of type DefinedTermBase, thus
294 * does not coincide with the return value of NamedArea and a ClassCastException is thrown.
295 *
296 * It is not clear why this only occurs in the editor and not in the webservice where the same
297 * method gets called and should lead to the same results.
298 *
299 * Seems to be a bigger problem although its origin is buggy behaviour of the javassist implementation.
300 */
301 @Deprecated
302 @Transient
303 public NamedArea getPartOfWorkaround(){
304 Object area = super.getPartOf();
305
306 if(!(area instanceof NamedArea)){
307 area = HibernateProxyHelper.deproxy(area, NamedArea.class);
308 }
309
310 return (NamedArea) area;
311 }
312
313 @Override
314 public void setPartOf(NamedArea partOf){
315 this.partOf = partOf;
316 }
317
318 @Override
319 @XmlElementWrapper(name = "Generalizations", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
320 @XmlElement(name = "GeneralizationOf", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
321 @XmlIDREF
322 @XmlSchemaType(name = "IDREF")
323 public Set<NamedArea> getGeneralizationOf(){
324 return super.getGeneralizationOf();
325 }
326
327 @Override
328 protected void setGeneralizationOf(Set<NamedArea> value){
329 super.setGeneralizationOf(value);
330 }
331
332 @Override
333 @XmlElementWrapper(name = "Includes", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
334 @XmlElement(name = "Include", namespace = "http://etaxonomy.eu/cdm/model/common/1.0")
335 @XmlIDREF
336 @XmlSchemaType(name = "IDREF")
337 public Set<NamedArea> getIncludes(){
338 return super.getIncludes();
339 }
340
341 @Override
342 protected void setIncludes(Set<NamedArea> includes) {
343 super.setIncludes(includes);
344 }
345
346 @Override
347 public NamedArea readCsvLine(Class<NamedArea> termClass, List<String> csvLine, Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
348 NamedArea newInstance = super.readCsvLine(termClass, csvLine, terms, abbrevAsId);
349
350 String levelString = csvLine.get(6);
351
352 if(levelString != null && levelString.length() != 0) {
353 UUID levelUuid = UUID.fromString(levelString);
354 NamedAreaLevel level = (NamedAreaLevel)terms.get(levelUuid);
355 newInstance.setLevel(level);
356 }
357
358 // String partOfString = csvLine.get(7);
359 //
360 // if(partOfString != null && partOfString.length() != 0) {
361 // UUID partOfUuid = UUID.fromString(partOfString);
362 // NamedArea partOf = (NamedArea)terms.get(partOfUuid);
363 // partOf.addIncludes(newInstance);
364 // }
365 return newInstance;
366 }
367
368 @Override
369 protected int partOfCsvLineIndex(){
370 return 7;
371 }
372
373
374 @Override
375 public void resetTerms(){
376 termMap = null;
377 tdwgAbbrevMap = null;
378 tdwglabelMap = null;
379 tdwgTermMap = null;
380 continentMap = null;
381 waterbodyMap = null;
382 }
383
384 @Deprecated //preliminary, will be removed in future
385 protected static NamedArea getContinentByUuid(UUID uuid){
386 if (continentMap == null){
387 return null;
388 }else{
389 return continentMap.get(uuid);
390 }
391 }
392
393 @Deprecated //preliminary, will be removed in future
394 protected static NamedArea getWaterbodyByUuid(UUID uuid){
395 if (waterbodyMap == null){
396 return null;
397 }else{
398 return waterbodyMap.get(uuid);
399 }
400 }
401
402 @Deprecated //preliminary, will be removed in future
403 protected static NamedArea getTdwgTermByUuid(UUID uuid){
404 if (tdwgTermMap == null){
405 DefaultTermInitializer vocabularyStore = new DefaultTermInitializer();
406 vocabularyStore.initialize();
407 }
408 return tdwgTermMap.get(uuid);
409 }
410
411 @Deprecated //preliminary, will be removed in future
412 public static NamedArea getAreaByTdwgAbbreviation(String tdwgAbbreviation){
413 if (tdwgAbbrevMap == null){
414 initTdwgMaps();
415 }
416 UUID uuid = tdwgAbbrevMap.get(tdwgAbbreviation);
417 if (uuid == null){
418 logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgAbbreviation));
419 return null;
420 }
421 return NamedArea.getTdwgTermByUuid(uuid);
422 }
423
424 @Deprecated //preliminary, will be removed in future
425 public static NamedArea getAreaByTdwgLabel(String tdwgLabel){
426 if (tdwglabelMap == null){
427 initTdwgMaps();
428 }
429 tdwgLabel = tdwgLabel.toLowerCase();
430 UUID uuid = tdwglabelMap.get(tdwgLabel);
431 if (uuid == null){
432 logger.info("Unknown TDWG area: " + CdmUtils.Nz(tdwgLabel));
433 return null;
434 }
435 return NamedArea.getTdwgTermByUuid(uuid);
436 }
437
438 @Deprecated //preliminary, will be removed in future
439 public static boolean isTdwgAreaLabel(String label){
440 label = (label == null? null : label.toLowerCase());
441 if (tdwglabelMap.containsKey(label)){
442 return true;
443 }else{
444 return false;
445 }
446 }
447
448 @Deprecated //preliminary, will be removed in future
449 public static boolean isTdwgAreaAbbreviation(String abbrev){
450 if (tdwgAbbrevMap.containsKey(abbrev)){
451 return true;
452 }else{
453 return false;
454 }
455 }
456
457 public static final NamedArea EUROPE(){
458 return getContinentByUuid(uuidEurope);
459 }
460
461 public static final NamedArea AFRICA(){
462 return getContinentByUuid(uuidAfrica);
463 }
464
465 public static final NamedArea ASIA_TEMPERATE(){
466 return getContinentByUuid(uuidAsiaTemperate);
467 }
468
469 public static final NamedArea ASIA_TROPICAL(){
470 return getContinentByUuid(uuidAsiaTropical);
471 }
472
473 public static final NamedArea NORTH_AMERICA(){
474 return getContinentByUuid(uuidNAmerica);
475 }
476
477 public static final NamedArea ANTARCTICA(){
478 return getContinentByUuid(uuidAntarctica);
479 }
480
481 public static final NamedArea SOUTH_AMERICA(){
482 return getContinentByUuid(uuidSAmerica);
483 }
484
485 public static final NamedArea AUSTRALASIA(){
486 return getContinentByUuid(uuidAustralasia);
487 }
488
489 public static final NamedArea PACIFIC(){
490 return getContinentByUuid(uuidPacific);
491 }
492
493
494 protected void setDefaultContinentTerms(TermVocabulary<NamedArea> termVocabulary) {
495 continentMap = new HashMap<UUID, NamedArea>();
496 for (NamedArea term : termVocabulary.getTerms()){
497 continentMap.put(term.getUuid(), term); //TODO casting
498 }
499 }
500
501 protected void setDefaultWaterbodyTerms(TermVocabulary<NamedArea> termVocabulary) {
502 waterbodyMap = new HashMap<UUID, NamedArea>();
503 for (NamedArea term : termVocabulary.getTerms()){
504 waterbodyMap.put(term.getUuid(), term); //TODO casting
505 }
506 }
507
508 protected void setTdwgDefaultTerms(TermVocabulary<NamedArea> tdwgTermVocabulary) {
509 tdwgTermMap = new HashMap<UUID, NamedArea>();
510 for (NamedArea term : tdwgTermVocabulary.getTerms()){
511 tdwgTermMap.put(term.getUuid(), term); //TODO casting
512 addTdwgArea(term);
513 }
514
515 }
516
517 protected static void addTdwgArea(NamedArea area){
518 if (area == null){
519 logger.warn("tdwg area is null");
520 return;
521 }
522 Language lang = Language.DEFAULT();
523 Representation representation = area.getRepresentation(lang);
524 String tdwgAbbrevLabel = representation.getAbbreviatedLabel();
525 String tdwgLabel = representation.getLabel().toLowerCase();
526 if (tdwgAbbrevLabel == null){
527 logger.warn("tdwgLabel = null");
528 return;
529 }
530 //init map
531 if (tdwgAbbrevMap == null){
532 tdwgAbbrevMap = new HashMap<String, UUID>();
533 }
534 if (tdwglabelMap == null){
535 tdwglabelMap = new HashMap<String, UUID>();
536 }
537 //add to map
538 tdwgAbbrevMap.put(tdwgAbbrevLabel, area.getUuid());
539 tdwglabelMap.put(tdwgLabel, area.getUuid());
540 //add type
541 area.setType(NamedAreaType.ADMINISTRATION_AREA());
542 //add level
543 if (tdwgAbbrevLabel.trim().length()== 1){
544 area.setLevel(NamedAreaLevel.TDWG_LEVEL1());
545 }else if (tdwgAbbrevLabel.trim().length()== 2){
546 area.setLevel(NamedAreaLevel.TDWG_LEVEL2());
547 }else if (tdwgAbbrevLabel.trim().length()== 3){
548 area.setLevel(NamedAreaLevel.TDWG_LEVEL3());
549 }else if (tdwgAbbrevLabel.trim().length()== 6){
550 area.setLevel(NamedAreaLevel.TDWG_LEVEL4());
551 }else {
552 logger.warn("Unknown TDWG Level " + tdwgAbbrevLabel + "! Unvalid string length (" + tdwgAbbrevLabel.length() +")");
553 }
554 }
555
556 private static void initTdwgMaps(){
557 tdwglabelMap = new HashMap<String, UUID>();
558 tdwgAbbrevMap = new HashMap<String, UUID>();
559 }
560
561
562
563 @Override
564 protected void setDefaultTerms(TermVocabulary<NamedArea> termVocabulary) {
565 if (termVocabulary.getUuid().equals(NamedArea.uuidTdwgAreaVocabulary)){
566 this.setTdwgDefaultTerms(termVocabulary);
567 }else if (termVocabulary.getUuid().equals(NamedArea.uuidContinentVocabulary)){
568 this.setDefaultContinentTerms(termVocabulary);
569 }else if (termVocabulary.getUuid().equals(NamedArea.uuidWaterbodyVocabulary)){
570 this.setDefaultWaterbodyTerms(termVocabulary);
571 }else{
572 termMap = new HashMap<UUID, NamedArea>();
573 for (NamedArea term : termVocabulary.getTerms()){
574 termMap.put(term.getUuid(), term);
575 }
576 }
577 }
578
579 // ************** Hierarchie List ****************************
580
581 /**
582 * This method returns a sorted tree structure which sorts areas by it's level and within the same level
583 * alphabetically (TODO to be tested).
584 * The structure returned is a tree with alternating nodes that represent an area and an areaLevel.
585 * This way also areas that have children belonging to different levels can be handled.<BR>
586 * The root node is always an empty area node which holds the list of top level areaLevels.
587 * AreaLevels with no level defined are handled as if they have a separate level (level="null").
588 *
589 * There is a somehow similar implementation in {@link eu.etaxonomy.cdm.api.service.DistributionTree}
590 *
591 * @param areaList
592 * @return
593 */
594 public static NamedAreaNode getHiearchieList(List<NamedArea> areaList){
595 NamedAreaNode result = new NamedAreaNode();
596 for (NamedArea area : areaList){
597 List<NamedArea> areaHierarchie = area.getAllLevelList();
598 mergeIntoResult(result, areaHierarchie);
599 }
600 return result;
601 }
602
603
604 public static class LevelNode {
605 NamedAreaLevel level;
606 List<NamedAreaNode> areaList = new ArrayList<NamedAreaNode>();
607
608 public NamedAreaNode add(NamedArea area) {
609 NamedAreaNode node = new NamedAreaNode();
610 node.area = area;
611 areaList.add(node);
612 return node;
613
614 }
615
616 public NamedAreaNode getNamedAreaNode(NamedArea area) {
617 for (NamedAreaNode node : areaList) {
618 if (node.area.equals(area)) {
619 return node;
620 }
621 }
622 return null;
623 }
624
625 ///****************** toString ***********************************************/
626
627 @Override
628 public String toString() {
629 return toString(false, 0);
630 }
631 public String toString(boolean recursive, int identation) {
632 String result = level == null? "" :level.getTitleCache();
633 if (recursive == false){
634 return result;
635 }else{
636 int areaSize = this.areaList.size();
637 if (areaSize > 0){
638 result = "\n" + StringUtils.leftPad("", identation) + result + "[";
639 }
640 boolean isFirst = true;
641 for (NamedAreaNode level: this.areaList){
642 if (isFirst){
643 isFirst = false;
644 }else{
645 result += ",";
646 }
647 result += level.toString(recursive, identation+1);
648 }
649 if (areaSize > 0){
650 result += "]";
651
652 }
653 return result;
654 }
655 }
656
657 }
658
659 public static class NamedAreaNode {
660 NamedArea area;
661 List<LevelNode> levelList = new ArrayList<LevelNode>();
662
663 public LevelNode getLevelNode(NamedAreaLevel level) {
664 for (LevelNode node : levelList) {
665 if (node.level != null && node.level.equals(level)) {
666 return node;
667 }
668 }
669 return null;
670 }
671
672 public List<NamedAreaNode> getList(NamedAreaLevel level) {
673 LevelNode node = getLevelNode(level);
674 if (node == null) {
675 return new ArrayList<NamedAreaNode>();
676 } else {
677 return node.areaList;
678 }
679 };
680
681 public boolean contains(NamedAreaLevel level) {
682 if (getList(level).size() > 0) {
683 return true;
684 } else {
685 return false;
686 }
687 }
688
689 public LevelNode add(NamedAreaLevel level) {
690 LevelNode node = new LevelNode();
691 node.level = level;
692 levelList.add(node);
693 return node;
694 }
695
696 @Override
697 public String toString() {
698 return toString(false, 0);
699 }
700
701 public String toString(boolean recursive, int identation) {
702 String result = "";
703 if (area != null) {
704 result = area.getTitleCache();
705 }
706 if (recursive){
707 int levelSize = this.levelList.size();
708 if (levelSize > 0){
709 result = "\n" + StringUtils.leftPad("", identation) + result + "[";
710 }
711 boolean isFirst = true;
712 for (LevelNode level: this.levelList){
713 if (isFirst){
714 isFirst = false;
715 }else{
716 result += ";";
717 }
718 result += level.toString(recursive, identation+1);
719 }
720 if (levelSize > 0){
721 result += "]";
722
723 }
724 return result;
725 }else{
726 int levelSize = this.levelList.size();
727 return result + "[" + levelSize + " sublevel(s)]";
728 }
729 }
730 }
731
732 private static void mergeIntoResult(NamedAreaNode root, List<NamedArea> areaHierarchie) {
733 if (areaHierarchie.isEmpty()) {
734 return;
735 }
736 NamedArea highestArea = areaHierarchie.get(0);
737 NamedAreaLevel level = highestArea.getLevel();
738 NamedAreaNode namedAreaNode;
739 if (! root.contains(level)) {
740 LevelNode node = root.add(level);
741 namedAreaNode = node.add(highestArea);
742 //NEW
743 // root.area = highestArea;
744 } else {
745 LevelNode levelNode = root.getLevelNode(level);
746 namedAreaNode = levelNode.getNamedAreaNode(highestArea);
747 if (namedAreaNode == null) {
748 namedAreaNode = levelNode.add(highestArea);
749 }
750 }
751 List<NamedArea> newList = areaHierarchie.subList(1, areaHierarchie.size());
752 mergeIntoResult(namedAreaNode, newList);
753
754 }
755
756 @Transient
757 public List<NamedArea> getAllLevelList() {
758 List<NamedArea> result = new ArrayList<NamedArea>();
759 NamedArea copyArea = this;
760 result.add(copyArea);
761 while (copyArea.getPartOf() != null) {
762 copyArea = copyArea.getPartOf();
763 result.add(0, copyArea);
764 }
765 return result;
766 }
767
768 // ******************* toString **********************************/
769
770 @Override
771 public String toString(){
772 String result, label, IdInVocabulary, level = "";
773
774 if (this.level != null){
775 level = this.level.getLabel();
776 }else{
777 level = "no level";
778 }
779 label = this.getLabel();
780 IdInVocabulary = getIdInVocabulary();
781 IdInVocabulary = IdInVocabulary != null ? '<' + IdInVocabulary + '>' : "";
782 result = "[" + level + ", " + IdInVocabulary + label + "]";
783
784 return result;
785 }
786
787
788
789 /**
790 * Returns the label of the named area together with the area level label and the abbreviated label.
791 * This is kind of a formatter method which may be moved to a better place in future.
792 * @param namedArea the area
793 * @param language the preferred language
794 * @return null if namedArea == null, the labelWithLevel otherwise
795 */
796 public static String labelWithLevel(NamedArea namedArea, Language language) {
797 if (namedArea == null){
798 return null;
799 }
800 NamedArea area = CdmBase.deproxy(namedArea, NamedArea.class);
801
802 StringBuilder title = new StringBuilder();
803 Representation representation = area.getPreferredRepresentation(language);
804 if (representation != null){
805 String areaString = getPreferredAreaLabel(namedArea, representation);
806
807 title.append(areaString);
808 }else if (area.isProtectedTitleCache()){
809 title.append(area.getTitleCache());
810 }else if (StringUtils.isNotBlank(area.getIdInVocabulary())){
811 title.append(area.getIdInVocabulary());
812 }
813 if (area.getLevel() == null){
814 title.append(" - ");
815 title.append(area.getClass().getSimpleName());
816 }else{
817 title.append(" - ");
818 Representation levelRepresentation = area.getLevel().getPreferredRepresentation(language);
819 String levelString = getPreferredAreaLabel(area.getLevel(), levelRepresentation);
820 title.append(levelString);
821 }
822 return title.toString();
823 }
824
825 /**
826 * @param definedTerm
827 * @param representation
828 * @return
829 */
830 private static String getPreferredAreaLabel(DefinedTermBase<?> definedTerm, Representation representation) {
831 String areaString = null;
832 if (representation != null){
833 areaString = representation.getLabel();
834 if (StringUtils.isBlank(areaString)){
835 areaString = representation.getAbbreviatedLabel();
836 }
837 if (StringUtils.isBlank(areaString)){
838 areaString = representation.getText();
839 }
840 }
841 if (StringUtils.isBlank(areaString)){
842 areaString = definedTerm == null ? null : definedTerm.getTitleCache();
843 }
844 if (StringUtils.isBlank(areaString)){
845 areaString = "no title";
846 }
847 return areaString;
848 }
849
850 //*********************************** CLONE *****************************************/
851
852 /**
853 * Clones <i>this</i> NamedArea. This is a shortcut that enables to create
854 * a new instance that differs only slightly from <i>this</i> NamedArea by
855 * modifying only some of the attributes.
856 *
857 * @see eu.etaxonomy.cdm.model.common.OrderedTermBase#clone()
858 * @see java.lang.Object#clone()
859 */
860 @Override
861 public Object clone() {
862 NamedArea result;
863
864 result = (NamedArea)super.clone();
865 //no changes to level, pointApproximation, shape, type, validPeriod and countries
866 return result;
867
868 }
869
870
871 }