View Javadoc

1   /*******************************************************************************
2    *   Gisgraphy Project 
3    * 
4    *   This library is free software; you can redistribute it and/or
5    *   modify it under the terms of the GNU Lesser General Public
6    *   License as published by the Free Software Foundation; either
7    *   version 2.1 of the License, or (at your option) any later version.
8    * 
9    *   This library is distributed in the hope that it will be useful,
10   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   *   Lesser General Public License for more details.
13   * 
14   *   You should have received a copy of the GNU Lesser General Public
15   *   License along with this library; if not, write to the Free Software
16   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
17   * 
18   *  Copyright 2008  Gisgraphy project 
19   *  David Masclet <davidmasclet@gisgraphy.com>
20   *  
21   *  
22   *******************************************************************************/
23  package com.gisgraphy.domain.geoloc.entity;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import javax.persistence.CascadeType;
29  import javax.persistence.Column;
30  import javax.persistence.Entity;
31  import javax.persistence.FetchType;
32  import javax.persistence.JoinColumn;
33  import javax.persistence.ManyToOne;
34  import javax.persistence.OneToMany;
35  import javax.persistence.Transient;
36  
37  import org.hibernate.annotations.Cache;
38  import org.hibernate.annotations.CacheConcurrencyStrategy;
39  import org.hibernate.annotations.Fetch;
40  import org.hibernate.annotations.FetchMode;
41  import org.hibernate.annotations.Index;
42  
43  import com.gisgraphy.helper.FeatureClassCodeHelper;
44  import com.gisgraphy.helper.IntrospectionIgnoredField;
45  
46  /**
47   * Represents a (sub) division of a {@link Country} (Region, Province, state,
48   * Department, and so on)<br>
49   * {@linkplain Adm} are in tree structure. An Adm can have some childs and Must
50   * have parent if the Level is > 1 (an Adm with level 1 is to be a 'ROOT')
51   * 
52   * @author <a href="mailto:david.masclet@gisgraphy.com">David Masclet</a>
53   */
54  @Entity
55  @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
56  public class Adm extends GisFeature {
57  
58      /**
59       * Constructor that populate the Adm with the gisFeature fields and set the
60       * level<br>
61       * <u>note</u> the feature Class will be set to 'A' and The featureCode
62       * will be set according to the Level (ex : ADM + level)
63       * 
64       * @param gisFeature
65       *                The gisFeature from which we want to populate the
66       *                {@linkplain Adm}
67       * @param level
68       *                The level of the Adm
69       */
70      public Adm(GisFeature gisFeature, Integer level) {
71  	super(gisFeature);
72  	setFeatureClass("A");
73  	setLevel(level);
74  	setFeatureCode("ADM" + getLevel());
75      }
76  
77      /**
78       * Constructor that create an Adm for the specified level<br>
79       * <u>note</u> the featureClass will be set to 'A' and The featureCode will
80       * be set according to the Level (ex : ADM + level)
81       * 
82       * @param level
83       *                The level of the Adm
84       */
85      public Adm(Integer level) {
86  	setLevel(level);
87  	setFeatureClass("A");
88  	setFeatureCode("ADM" + getLevel());
89      }
90  
91      /**
92       * Default constructor (Needed by CGLib)
93       */
94      protected Adm() {
95  	super();
96      }
97  
98      /**
99       * Check that the countryCode and the admXcode are correctly set according
100      * to the level of the {@linkplain Adm}
101      */
102     @Transient
103     public boolean isConsistentForLevel() {
104 	if (getCountryCode() == null) {
105 	    return false;
106 	}
107 	if (this.getLevel() == 1 && this.getAdm1Code() == null) {
108 	    return false;
109 	} else if (this.getLevel() == 2
110 		&& (this.getAdm1Code() == null || this.getAdm2Code() == null)) {
111 	    return false;
112 	} else if (this.getLevel() == 3
113 		&& (this.getAdm1Code() == null || this.getAdm2Code() == null || this
114 			.getAdm3Code() == null)) {
115 	    return false;
116 	} else if (this.getLevel() == 4
117 		&& (this.getAdm1Code() == null || this.getAdm2Code() == null
118 			|| this.getAdm3Code() == null || this.getAdm4Code() == null)) {
119 	    return false;
120 	}
121 	return true;
122     }
123 
124     private Integer level;
125 
126     @IntrospectionIgnoredField
127     private Adm parent;
128 
129     private List<Adm> children;
130 
131     /**
132      * @return The Level Of The Adm
133      */
134     @Column(nullable = false)
135     @Index(name = "admLevel")
136     public Integer getLevel() {
137 	return level;
138     }
139 
140     /**
141      * Set the level and Check that 1<=level<=4. If Not throw an
142      * {@link IllegalArgumentException}
143      * 
144      * @param level
145      *                The Level to Set
146      * @throws IllegalArgumentException
147      *                 If level is not correct
148      */
149     public void setLevel(Integer level) {
150 	if (level < 1 || level > 4) {
151 	    throw new IllegalArgumentException(
152 		    "The level of an Adm can not be " + level
153 			    + ". it must be beetween 1 and 4");
154 	}
155 	this.level = level;
156     }
157 
158     /**
159      * Do a double set : Add (not replace ! ) The child to the current Adm and
160      * set the current Adm as the Parent of the specified Child.
161      * 
162      * @param child
163      *                The child to add is not equals to the level of this Adm+1
164      */
165     public void addChild(Adm child) {
166 	if (child == null) {
167 	    logger.info("Could not add a null child");
168 	    throw new IllegalArgumentException("Could not add a null child to "
169 		    + this);
170 	}
171 	if (child.getLevel() != getLevel() + 1) {
172 	    throw new IllegalArgumentException("a child of level "
173 		    + child.getLevel() + " : " + child
174 		    + " should not be added to an Adm of level " + getLevel()
175 		    + " : " + this.toString() + " but will be added");
176 	}
177 	List<Adm> currentChilds = getChildren();
178 	if (currentChilds == null) {
179 	    currentChilds = new ArrayList<Adm>();
180 	}
181 	currentChilds.add(child);
182 	this.setChildren(currentChilds);
183 	child.setParent(this);
184 
185     }
186 
187     /**
188      * Do a double set : Add (not replace !) the children to the current Adm and
189      * set the current Adm as the parent of the specified Children
190      * 
191      * @see #addChild(Adm)
192      * @param children
193      *                The children to add
194      */
195     public void addChildren(List<Adm> children) {
196 	if (children != null) {
197 	    for (Adm child : children) {
198 		addChild(child);
199 	    }
200 	}
201 	;
202     }
203 
204     /**
205      * Return the Adms of the higher Level
206      * 
207      * @return The Adms of the higher Level <br>
208      *         <b>Example</b> Returns the Adm(s) with level 2 if the current
209      *         Adm has a level equals to 1
210      */
211     @OneToMany(cascade = { CascadeType.ALL }, mappedBy = "parent")
212     @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
213     @Fetch(FetchMode.SELECT)
214     public List<Adm> getChildren() {
215 	return children;
216     }
217 
218     /**
219      * Set the Adms of a higher level
220      * 
221      * @param children
222      *                the children for the current Adm
223      */
224     // @throws IllegalArgumentException If the children are are not equals to
225     // the level of this Adm+1
226     public void setChildren(List<Adm> children) {
227 	// TODO v2 DSL
228 	/*
229 	 * try { if (children != null ){ for (Adm child : children){ if
230 	 * (child.getLevel()!= getLevel()+1){ throw new
231 	 * IllegalArgumentException("Could not add a child of level
232 	 * "+child.getLevel()+" to an Adm of level "+getLevel()); } } }
233 	 */
234 	this.children = children;
235 
236     }
237 
238     /**
239      * Returns The parent Adm in the Adm tree structure
240      * 
241      * @return The parent Adm (with lower Level)
242      */
243     @ManyToOne(fetch = FetchType.EAGER)
244     @JoinColumn(nullable = true, name = "parent")
245     @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
246     public Adm getParent() {
247 	return parent;
248     }
249 
250     /**
251      * Set the parent Adm in the tree structure
252      * 
253      * @param parent
254      *                the Parent Adm to set
255      */
256     // @throws IllegalArgumentException if the parent is not equals to the level
257     // of this Adm-1
258     public void setParent(Adm parent) {
259 	// TODO v2 dsl
260 	/*
261 	 * if (parent != null && parent.getLevel()!=getLevel()-1 ){ throw new
262 	 * IllegalArgumentException("Could not add a null child"); }
263 	 */
264 	this.parent = parent;
265     }
266 
267     private static boolean isAdmCodeEmpty(String admCode) {
268 	if (admCode == null || admCode.trim().length() == 0) {
269 	    return true;
270 	}
271 	return false;
272     }
273 
274     /**
275      * Determine the Level from the given codes
276      * 
277      * @param adm1Code
278      *                The Adm1Code of the Adm to test
279      * @param adm2Code
280      *                The Adm2Code of the Adm to test
281      * @param adm3Code
282      *                The Adm3Code of the Adm to test
283      * @param adm4Code
284      *                The Adm4Code of the Adm to test
285      * @return the processed Level or 0 if the level can not be determine or all
286      *         the code are null
287      */
288     public static int getProcessedLevelFromCodes(String adm1Code,
289 	    String adm2Code, String adm3Code, String adm4Code) {
290 	if (!isAdmCodeEmpty(adm1Code)) {
291 	    if (!isAdmCodeEmpty(adm2Code)) {
292 		if (!isAdmCodeEmpty(adm3Code)) {
293 		    if (!isAdmCodeEmpty(adm4Code)) {
294 			// adm1,adm2,adm3,adm4
295 			return 4;
296 		    } else {
297 			// adm1,adm2,adm3,null
298 			return 3;
299 		    }
300 		} else {
301 		    // adm1,Adm2, null..
302 		    return 2;
303 		}
304 	    } else {
305 		// adm1,null...
306 		return 1;
307 	    }
308 	} else {
309 	    // if adm1 is empty can not retrieve any Adm
310 	    return 0;
311 	}
312 
313     }
314 
315     /**
316      * Determine the level from a featureClass and a featureCode.<br/> e.g :
317      * featureClass=A and featureCode=ADM3 will return 3 .<br/> featureClass=P
318      * and featureCode=ADM4 will return 0 because P_ADM4 is not of ADM type this
319      * method is case sensitive
320      * 
321      * @param featureClass
322      *                The featureClass of the Adm to test
323      * @param featureCode
324      *                The featureCode of the Adm to Test
325      * @return the Level of the Adm or 0 if the level can not be determine
326      */
327     public static int getProcessedLevelFromFeatureClassCode(
328 	    String featureClass, String featureCode) {
329 	if (FeatureClassCodeHelper.is_Adm(featureClass, featureCode)) {
330 	    int level = 0;
331 	    try {
332 		level = new Integer(featureCode.substring(3)).intValue();
333 	    } catch (NumberFormatException e) {
334 	    }
335 	    return level;
336 
337 	} else {
338 	    return 0;
339 	}
340 
341     }
342 
343     /*
344      * (non-Javadoc)
345      * 
346      * @see com.gisgraphy.domain.geoloc.entity.GisFeature#hashCode()
347      */
348     @Override
349     public int hashCode() {
350 	final int PRIME = 31;
351 	int result = super.hashCode();
352 	result = PRIME * result
353 		+ ((getAdm1Code() == null) ? 0 : getAdm1Code().hashCode());
354 	result = PRIME * result
355 		+ ((getAdm2Code() == null) ? 0 : getAdm2Code().hashCode());
356 	result = PRIME * result
357 		+ ((getAdm3Code() == null) ? 0 : getAdm3Code().hashCode());
358 	result = PRIME * result
359 		+ ((getAdm4Code() == null) ? 0 : getAdm4Code().hashCode());
360 	result = PRIME * result + ((level == null) ? 0 : level.hashCode());
361 	return result;
362     }
363 
364     /**
365      * @see com.gisgraphy.domain.geoloc.entity.GisFeature#equals(java.lang.Object)
366      */
367     @Override
368     public boolean equals(Object obj) {
369 	if (this == obj) {
370 	    return true;
371 	}
372 	if (!super.equals(obj)) {
373 	    return false;
374 	}
375 	if (getClass() != obj.getClass()) {
376 	    return false;
377 	}
378 	final Adm other = (Adm) obj;
379 	if (level == null) {
380 	    if (other.level != null) {
381 		return false;
382 	    }
383 	} else if (!level.equals(other.level)) {
384 	    return false;
385 	}
386 	if (getCountryCode() == null) {
387 	    if (other.getCountryCode() != null) {
388 		return false;
389 	    }
390 	} else if (!getCountryCode().equals(other.getCountryCode())) {
391 	    return false;
392 	}
393 	if (getAdm1Code() == null) {
394 	    if (other.getAdm1Code() != null) {
395 		return false;
396 	    }
397 	} else if (!getAdm1Code().equals(other.getAdm1Code())) {
398 	    return false;
399 	}
400 	if (getAdm2Code() == null) {
401 	    if (other.getAdm2Code() != null) {
402 		return false;
403 	    }
404 	} else if (!getAdm2Code().equals(other.getAdm2Code())) {
405 	    return false;
406 	}
407 	if (getAdm3Code() == null) {
408 	    if (other.getAdm3Code() != null) {
409 		return false;
410 	    }
411 	} else if (!getAdm3Code().equals(other.getAdm3Code())) {
412 	    return false;
413 	}
414 	if (getAdm4Code() == null) {
415 	    if (other.getAdm4Code() != null) {
416 		return false;
417 	    }
418 	} else if (!getAdm4Code().equals(other.getAdm4Code())) {
419 	    return false;
420 	}
421 	return true;
422     }
423 
424     /*
425      * (non-Javadoc)
426      * 
427      * @see com.gisgraphy.domain.geoloc.entity.GisFeature#toString()
428      */
429     @Override
430     public String toString() {
431 	return "Adm[" + getCountryCode() + "." + getAdm1Code() + "."
432 		+ getAdm2Code() + "." + getAdm3Code() + "." + getAdm4Code()
433 		+ "][level=" + getLevel() + "][" + getFeatureId() + "]["
434 		+ getName() + "]";
435     }
436 
437 }