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