108befda7a077f3bc579e02befe09fb8c600e883
[taxeditor.git] / taxeditor-store / src / main / java / eu / etaxonomy / taxeditor / model / FeatureNodeContainer.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.taxeditor.model;
12
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Comparator;
16 import java.util.List;
17 import java.util.Set;
18
19 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
20 import eu.etaxonomy.cdm.model.description.DescriptionBase;
21 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
22 import eu.etaxonomy.cdm.model.description.Feature;
23 import eu.etaxonomy.cdm.model.description.FeatureNode;
24 import eu.etaxonomy.cdm.model.description.FeatureTree;
25 import eu.etaxonomy.cdm.model.description.TaxonDescription;
26
27 /**
28 * This class is a simple container to allow generation of a datastructure that
29 * describes a feature tree according to a specific description element, i.e a tree
30 * structure that halds only the {@link FeatureNode}s that are relevant for a specific
31 * {@link TaxonDescription} as well as the {@link DescriptionElementBase} at the leaf level.
32 *
33 * This kind of datastructure is needed by interface elements such as viewers and greatly simplify
34 * the handling of {@link FeatureTree}s in conjunction with {@link TaxonDescription}s.
35 *
36 * @author n.hoffmann
37 * @created Sep 20, 2010
38 * @version 1.0
39 */
40 public class FeatureNodeContainer{
41
42
43
44 private FeatureNodeContainer parent;
45
46
47
48 private FeatureNode featureNode;
49 private List<FeatureNodeContainer> children = new ArrayList<FeatureNodeContainer>();
50 private List<DescriptionElementBase> descriptionElements = new ArrayList<DescriptionElementBase>();
51
52 private FeatureNodeContainerTree containerTree;
53
54
55 /**
56 * @param description
57 */
58 protected FeatureNodeContainer(FeatureNodeContainerTree containerTree) {
59 this.containerTree = containerTree;
60 this.containerTree.addContainer(this);
61 }
62
63
64 /**
65 * Recursively traverse a branch of a feature tree and check if there are
66 *
67 * @param featureNode
68 * @param description
69 * @return
70 */
71 protected void findLeaves(FeatureNode featureNode) {
72 if(featureNode.isLeaf()){
73 buildLeaf(featureNode);
74 }else{
75 for(FeatureNode childNode : featureNode.getChildren()){
76 findLeaves(childNode);
77 }
78 }
79 }
80
81 /**
82 *
83 * @param featureNode
84 * @param description
85 * @return
86 */
87 private void buildLeaf(FeatureNode featureNode){
88 // get feature node container for the given feature
89 FeatureNodeContainer container = containerTree.getFeatureNodeContainerByFeature(featureNode.getFeature());
90
91 // get description elements for the given feature
92 List<DescriptionElementBase> elements = containerTree.getDescriptionsElementsForFeature(featureNode.getFeature());
93 // no description elements, so we should also remove the feature node container
94 if(elements.isEmpty()){
95 if(container != null){
96 container.remove();
97 }
98 }
99 // there are description elements
100 else{
101 if(container == null){
102 container = new FeatureNodeContainer(containerTree);
103 container.setFeatureNode(featureNode);
104 // build the branch up to root level
105 container.buildBranch();
106 }
107 // add description elements to the feature node container
108 container.setDescriptionElements(elements);
109 }
110 }
111
112 /**
113 *
114 */
115 private void remove() {
116 if(getParent() != null){
117 if(getParent().getChildren().size() == 1){
118 getParent().remove();
119 }
120 getParent().removeChild(this);
121 }
122 }
123
124
125 /**
126 * @param featureNodeContainer
127 */
128 private void removeChild(FeatureNodeContainer featureNodeContainer) {
129 children.remove(featureNodeContainer);
130 }
131
132
133 /**
134 * Recursively
135 *
136 * @param featureNodeMap
137 * @return
138 */
139 private void buildBranch(){
140 if(getParent() == null){
141 FeatureNode parentFeatureNode = getFeatureNode().getParent();
142
143 if(parentFeatureNode.isRoot()){
144 containerTree.getRoot().addChild(this);
145 }else{
146 FeatureNodeContainer parentContainer = new FeatureNodeContainer(containerTree);
147 parentContainer.setFeatureNode(parentFeatureNode);
148
149 parentContainer.addChild(this);
150
151 parentContainer.buildBranch();
152
153 }
154 }
155 }
156
157 /**
158 * <p>Getter for the field <code>children</code>.</p>
159 *
160 * @return a {@link java.util.List} object.
161 */
162 public List<FeatureNodeContainer> getChildren() {
163 return children;
164 }
165
166 /**
167 * Sets the list of children of this containers children
168 *
169 * @param children a {@link java.util.List} object.
170 * @throws java.lang.IllegalStateException when <code>this</code> container contains a description element.
171 */
172 public void setChildren(List<FeatureNodeContainer> children) {
173 if(descriptionElements.isEmpty()){
174 this.children = children;
175 }else{
176 throw new IllegalStateException("Container may not have a description element set when setting children.");
177 }
178 }
179
180 /**
181 * Adds a child container to the list of this containers children
182 *
183 * @param container a {@link eu.etaxonomy.taxeditor.model.FeatureNodeContainer} object.
184 * @throws java.lang.IllegalStateException when <code>this</code> container contains a description element.
185 */
186 public void addChild(FeatureNodeContainer container){
187 if(descriptionElements.isEmpty()){
188 children.add(container);
189 container.setParent(this);
190 }else{
191 throw new IllegalStateException("Container may not have a description element set when adding children.");
192 }
193 }
194
195 public void addDescriptionElement(DescriptionElementBase descriptionElement){
196 descriptionElements.add(descriptionElement);
197 }
198
199 public void removeDescriptionElement(DescriptionElementBase descriptionElement){
200 descriptionElements.remove(descriptionElement);
201 }
202
203 /**
204 * If {@link #isLeaf()} is true, i.e. this container should have elements, returns the list of description elements.
205 *
206 * @return a {@link java.util.List} object.
207 */
208 public List<DescriptionElementBase> getDescriptionElements() {
209 return descriptionElements;
210 }
211
212 /**
213 * Cumulates description elements for <code>this</code> container as well as child feature nodes recursively,
214 * thus returning a list of description elements for the branch of the feature tree starting with <code>this</code>
215 * node.
216 *
217 * @return a {@link java.util.List} object.
218 */
219 public List<DescriptionElementBase> getDescriptionElementsForEntireBranch(){
220 return getDescriptionElementsRecursively(new ArrayList<DescriptionElementBase>());
221 }
222
223 private List<DescriptionElementBase> getDescriptionElementsRecursively(List<DescriptionElementBase> descriptionElements){
224 if(isLeaf()){
225 descriptionElements.addAll(getDescriptionElements());
226 }else{
227 for(FeatureNodeContainer container : getChildren()){
228 container.getDescriptionElementsRecursively(descriptionElements);
229 }
230 }
231 return descriptionElements;
232 }
233
234 protected List<FeatureNodeContainer> getLeafs(){
235 List<FeatureNodeContainer> leafs = new ArrayList<FeatureNodeContainer>();
236
237 if(isLeaf()){
238 leafs.add(this);
239 }else{
240 for(FeatureNodeContainer container : getChildren()){
241 leafs.addAll(container.getLeafs());
242 }
243 }
244 return leafs;
245 }
246
247 /**
248 * Set the description element
249 *
250 * @throws java.lang.IllegalStateException when <code>this</code> container contains child container.
251 * @param descriptionElements a {@link java.util.List} object.
252 */
253 public void setDescriptionElements(List<DescriptionElementBase> descriptionElements) {
254 if(children.isEmpty()){
255 this.descriptionElements = descriptionElements;
256 }else{
257 throw new IllegalStateException("Container may not contain child container when adding description elements.");
258 }
259 }
260
261 /**
262 * If the container is a leaf, it will hold a description element and no child containers
263 *
264 * @return a boolean.
265 */
266 public boolean isLeaf(){
267 return ! descriptionElements.isEmpty() && children.isEmpty();
268 }
269
270 /**
271 * <p>Setter for the field <code>featureNode</code>.</p>
272 *
273 * @param featureNode a {@link eu.etaxonomy.cdm.model.description.FeatureNode} object.
274 */
275 public void setFeatureNode(FeatureNode featureNode) {
276 this.featureNode = featureNode;
277 }
278
279 /**
280 * <p>Getter for the field <code>featureNode</code>.</p>
281 *
282 * @return a {@link eu.etaxonomy.cdm.model.description.FeatureNode} object.
283 */
284 public FeatureNode getFeatureNode() {
285 return featureNode;
286 }
287
288 /**
289 * <p>getFeature</p>
290 *
291 * @return a {@link eu.etaxonomy.cdm.model.description.Feature} object.
292 */
293 public Feature getFeature(){
294 if(featureNode != null){
295 return featureNode.getFeature();
296 }
297 return null;
298 }
299
300 /**
301 * <p>Getter for the field <code>description</code>.</p>
302 *
303 * @return a {@link eu.etaxonomy.cdm.model.description.DescriptionBase} object.
304 */
305 public DescriptionBase getDescription(){
306 return containerTree.getDescription();
307 }
308
309 public FeatureNodeContainerTree getContainerTree(){
310 return containerTree;
311 }
312
313
314 /**
315 *
316 */
317 public void clear() {
318 children.clear();
319 descriptionElements.clear();
320 }
321
322
323 /**
324 * @return
325 */
326 public boolean isEmpty() {
327 return children.isEmpty() && descriptionElements.isEmpty();
328 }
329
330 /**
331 * @return the parent
332 */
333 public FeatureNodeContainer getParent() {
334 return parent;
335 }
336
337
338 /**
339 * @param parent the parent to set
340 */
341 public void setParent(FeatureNodeContainer parent) {
342 this.parent = parent;
343 }
344 }