minor fixes and new method for taxonomic tree
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / TaxonNode.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.model.taxon;
12
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17
18 import javax.persistence.Entity;
19 import javax.persistence.FetchType;
20 import javax.persistence.ManyToOne;
21 import javax.persistence.OneToMany;
22 import javax.persistence.Transient;
23 import javax.xml.bind.annotation.XmlAccessType;
24 import javax.xml.bind.annotation.XmlAccessorType;
25 import javax.xml.bind.annotation.XmlElement;
26 import javax.xml.bind.annotation.XmlElementWrapper;
27 import javax.xml.bind.annotation.XmlIDREF;
28 import javax.xml.bind.annotation.XmlRootElement;
29 import javax.xml.bind.annotation.XmlSchemaType;
30 import javax.xml.bind.annotation.XmlType;
31
32 import org.apache.log4j.Logger;
33 import org.hibernate.annotations.Cascade;
34 import org.hibernate.annotations.CascadeType;
35 import org.hibernate.envers.Audited;
36
37 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
38 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
39
40 /**
41 * @author a.mueller
42 * @created 31.03.2009
43 * @version 1.0
44 */
45 @XmlAccessorType(XmlAccessType.FIELD)
46 @XmlType(name = "TaxonNode", propOrder = {
47 "taxon",
48 "parent",
49 "taxonomicTree",
50 "childNodes",
51 "referenceForParentChildRelation",
52 "microReferenceForParentChildRelation",
53 "countChildren",
54 "synonymToBeUsed"
55 })
56 @XmlRootElement(name = "TaxonNode")
57 @Entity
58 @Audited
59 public class TaxonNode extends AnnotatableEntity {
60 private static final long serialVersionUID = -4743289894926587693L;
61 @SuppressWarnings("unused")
62 private static final Logger logger = Logger.getLogger(TaxonNode.class);
63
64 @XmlElement(name = "taxon")
65 @XmlIDREF
66 @XmlSchemaType(name = "IDREF")
67 @ManyToOne(fetch = FetchType.LAZY)
68 @Cascade({CascadeType.SAVE_UPDATE})
69 private Taxon taxon;
70
71
72 @XmlElement(name = "parent")
73 @XmlIDREF
74 @XmlSchemaType(name = "IDREF")
75 @ManyToOne(fetch = FetchType.LAZY)
76 @Cascade({CascadeType.SAVE_UPDATE})
77 private TaxonNode parent;
78
79
80 @XmlElement(name = "taxonomicTree")
81 @XmlIDREF
82 @XmlSchemaType(name = "IDREF")
83 @ManyToOne(fetch = FetchType.LAZY)
84 @Cascade({CascadeType.SAVE_UPDATE})
85 private TaxonomicTree taxonomicTree;
86
87 @XmlElementWrapper(name = "childNodes")
88 @XmlElement(name = "childNode")
89 @XmlIDREF
90 @XmlSchemaType(name = "IDREF")
91 @OneToMany(mappedBy="parent", fetch=FetchType.LAZY)
92 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
93 private Set<TaxonNode> childNodes = new HashSet<TaxonNode>();
94
95 @XmlElement(name = "reference")
96 @XmlIDREF
97 @XmlSchemaType(name = "IDREF")
98 @ManyToOne(fetch = FetchType.LAZY)
99 @Cascade({CascadeType.SAVE_UPDATE})
100 private ReferenceBase referenceForParentChildRelation;
101
102 @XmlElement(name = "microReference")
103 private String microReferenceForParentChildRelation;
104
105 @XmlElement(name = "countChildren")
106 private int countChildren;
107
108 // private Taxon originalConcept;
109 // //or
110 @XmlElement(name = "synonymToBeUsed")
111 @XmlIDREF
112 @XmlSchemaType(name = "IDREF")
113 @ManyToOne(fetch = FetchType.LAZY)
114 @Cascade({CascadeType.SAVE_UPDATE})
115 private Synonym synonymToBeUsed;
116
117
118 protected TaxonNode(){
119 super();
120 }
121
122 //to create nodes either use TaxonomicView.addRoot() or TaxonNode.addChild();
123 protected TaxonNode (Taxon taxon, TaxonomicTree taxonomicTree){
124 setTaxon(taxon);
125 setTaxonomicView(taxonomicTree);
126 }
127
128
129
130 //************************ METHODS **************************/
131
132 public TaxonNode addChild(Taxon taxon){
133 return addChild(taxon, null, null, null);
134 }
135
136 public TaxonNode addChild(Taxon taxon, ReferenceBase ref, String microReference){
137 return addChild(taxon, ref, microReference, null);
138 }
139
140 public TaxonNode addChild(Taxon taxon, ReferenceBase ref, String microReference, Synonym synonymUsed){
141 if (this.getTaxonomicTree().isTaxonInTree(taxon)){
142 throw new IllegalArgumentException("Taxon may not be twice in a taxonomic view");
143 }
144 TaxonNode childNode = new TaxonNode(taxon, this.getTaxonomicTree());
145 addChildNote(childNode, ref, microReference, synonymUsed);
146 return childNode;
147 }
148
149 protected void addChildNote(TaxonNode childNode, ReferenceBase ref, String microReference, Synonym synonymUsed){
150 if (! childNode.getTaxonomicTree().equals(this.getTaxonomicTree())){
151 throw new IllegalArgumentException("addChildNote(): both nodes must be part of the same view");
152 }
153 childNode.setParent(this);
154 childNodes.add(childNode);
155 this.countChildren++;
156 childNode.setReferenceForParentChildRelation(ref);
157 childNode.setMicroReferenceForParentChildRelation(microReference);
158 childNode.setSynonymToBeUsed(synonymUsed);
159 }
160
161 /**
162 * This removes recursively all child nodes from this node and from this taxonomic view.
163 * TODO remove orphan nodes completely
164 *
165 * @param node
166 * @return
167 */
168 public boolean removeChild(TaxonNode node){
169 boolean result = false;
170 if (node != null){
171 for (TaxonNode grandChildNode : node.getChildNodes()){
172 node.removeChild(grandChildNode);
173 }
174 result = childNodes.remove(node);
175 this.countChildren--;
176 if (this.countChildren < 0){
177 throw new IllegalStateException("children count must not be negative ");
178 }
179 node.getTaxon().removeTaxonNode(node);
180 node.setParent(null);
181 node.setTaxonomicView(null);
182 node.setTaxon(null);
183 }
184 return result;
185 }
186
187 /**
188 * Remove this taxonNode From its taxonomic view.
189 *
190 * @return true on success
191 */
192 public boolean remove(){
193 if(isRootNode()){
194 return taxonomicTree.removeRoot(this);
195 }else{
196 return getParent().removeChild(this);
197 }
198 }
199
200 //*********** GETTER / SETTER ***********************************/
201
202 public Taxon getTaxon() {
203 return taxon;
204 }
205 protected void setTaxon(Taxon taxon) {
206 this.taxon = taxon;
207 if (taxon != null){
208 taxon.addTaxonNode(this);
209 }
210 }
211 public TaxonNode getParent() {
212 return parent;
213 }
214 protected void setParent(TaxonNode parent) {
215 this.parent = parent;
216 }
217 public TaxonomicTree getTaxonomicTree() {
218 return taxonomicTree;
219 }
220 //invisible part of the bidirectional relationship, for public use TaxonomicView.addRoot() or TaxonNode.addChild()
221 protected void setTaxonomicView(TaxonomicTree taxonomicTree) {
222 this.taxonomicTree = taxonomicTree;
223 }
224 public Set<TaxonNode> getChildNodes() {
225 return childNodes;
226 }
227
228 /**
229 * Returns a set containing this node and all nodes that are descendants of this node
230 *
231 * @return
232 */
233 protected Set<TaxonNode> getAllNodes(){
234 Set<TaxonNode> nodeSet = new HashSet<TaxonNode>();
235
236 nodeSet.add(this);
237
238 for(TaxonNode childNode : getChildNodes()){
239 nodeSet.addAll(childNode.getAllNodes());
240 }
241
242 return nodeSet;
243 }
244
245 // protected void setChildNodes(List<TaxonNode> childNodes) {
246 // this.childNodes = childNodes;
247 // }
248 public ReferenceBase getReferenceForParentChildRelation() {
249 return referenceForParentChildRelation;
250 }
251 public void setReferenceForParentChildRelation(
252 ReferenceBase referenceForParentChildRelation) {
253 this.referenceForParentChildRelation = referenceForParentChildRelation;
254 }
255 public String getMicroReferenceForParentChildRelation() {
256 return microReferenceForParentChildRelation;
257 }
258 public void setMicroReferenceForParentChildRelation(
259 String microReferenceForParentChildRelation) {
260 this.microReferenceForParentChildRelation = microReferenceForParentChildRelation;
261 }
262 public int getCountChildren() {
263 return countChildren;
264 }
265 public void setCountChildren(int countChildren) {
266 this.countChildren = countChildren;
267 }
268 // public Taxon getOriginalConcept() {
269 // return originalConcept;
270 // }
271 // public void setOriginalConcept(Taxon originalConcept) {
272 // this.originalConcept = originalConcept;
273 // }
274 public Synonym getSynonymToBeUsed() {
275 return synonymToBeUsed;
276 }
277 public void setSynonymToBeUsed(Synonym synonymToBeUsed) {
278 this.synonymToBeUsed = synonymToBeUsed;
279 }
280
281 /**
282 * Whether this TaxonNode is a root node
283 * @return
284 */
285 public boolean isRootNode(){
286 return parent == null;
287 }
288
289 /**
290 * Whether this TaxonNode is a descendant of the given TaxonNode
291 *
292 * @param possibleParent
293 * @return true if this is a descendant
294 */
295 @Transient
296 public boolean isDescendant(TaxonNode possibleParent){
297 return possibleParent.getAllNodes().contains(this);
298 }
299
300 }