root/trunk/cdmlib/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/PolytomousKeyNode.java

Revision 13024, 23.1 kB (checked in by a.mueller, 8 months ago)

commented annotations on PolytomousKeyNode?.key

Line 
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
10package eu.etaxonomy.cdm.model.description;
11
12import java.util.ArrayList;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16import java.util.Map.Entry;
17
18import javax.persistence.Entity;
19import javax.persistence.FetchType;
20import javax.persistence.JoinColumn;
21import javax.persistence.ManyToOne;
22import javax.persistence.OneToMany;
23import javax.persistence.OrderBy;
24import javax.persistence.Transient;
25import javax.validation.constraints.NotNull;
26import javax.xml.bind.annotation.XmlAccessType;
27import javax.xml.bind.annotation.XmlAccessorType;
28import javax.xml.bind.annotation.XmlElement;
29import javax.xml.bind.annotation.XmlElementWrapper;
30import javax.xml.bind.annotation.XmlIDREF;
31import javax.xml.bind.annotation.XmlRootElement;
32import javax.xml.bind.annotation.XmlSchemaType;
33import javax.xml.bind.annotation.XmlType;
34import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
35
36import org.apache.log4j.Logger;
37import org.hibernate.annotations.Cascade;
38import org.hibernate.annotations.CascadeType;
39import org.hibernate.annotations.IndexColumn;
40import org.hibernate.envers.Audited;
41
42import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
43import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
44import eu.etaxonomy.cdm.model.common.Language;
45import eu.etaxonomy.cdm.model.common.LanguageString;
46import eu.etaxonomy.cdm.model.common.MultilanguageText;
47import eu.etaxonomy.cdm.model.common.VersionableEntity;
48import eu.etaxonomy.cdm.model.taxon.Taxon;
49
50/**
51 * The class represents a node within a {@link PolytomousKey polytomous key}
52 * structure. The structure such key is a directed acyclic graph where as
53 * instances of <code>PolytomousKeyNode</code>, {@Taxon Taxa}, or even
54 * of other {@link PolytomousKey PolytomousKeys} are forming the nodes. A
55 * <code>PolytomousKeyNode</code> can have multiple kinds of edges:
56 * <p>
57 * <h4>Edges</h4>
58 * In very rare cases a polytomous key node can have same time an edge to a
59 * taxon and other edges to futher child nodes. In this case the determination
60 * process may be terminated at this taxon or can proceed if a more accurate
61 * determination is wanted.
62 * <li>{@link #getChildren() children}:<br>
63 * One or multiple subordinate <code>PolytomousKeyNodes</code>. The source node
64 * poses a {@link #getQuestion question}, to which the children are providing
65 * predefined answers by their {@link #getStatement() statement}. It is also
66 * possible that the children's {@link #getStatement() statements} are
67 * formulated in a way which makes an explicit question at the source node
68 * obsolete. In this case the question may be empty. There is furthermore the
69 * {@link #getFeature() feature} property which also plays the role as question.
70 * The <code>feature</code> property will most likely be used if the key has
71 * been generated automatically. Hand written keys will have a
72 * <code>question</code>. An existing question should always be given
73 * <b>priority</b> over the <code>feature</code>.
74 * <p>
75 * <b>Special case:</b> <i>Child nodes with empty statements but taxa as
76 * leaf</i> are to treated as if all those taxa where direct children of the
77 * source node. That is the nodes in with empty statements are not to be shown,
78 * they are only a structural element to connect multiple taxa to a single node,
79 * which is otherwise not possible.</li>
80 * <li>{@link #getOtherNode() otherNode}: <br>
81 * Without this kind of edge which points to other nodes in the same graph edge
82 * the key would be a perfect tree structure.</li>
83 * <li>{@link #getSubkey() subkey}:<br>
84 * Connects two key with each other. The path in the decision graph spans over
85 * multiple keys.</li>
86 * <li>{@link #getTaxon() taxon}:<br>
87 * The taxa are the final leaf nodes of the decision graph, the decision process
88 * ends and the respective object has been determined as being a representative
89 * of this {@link Taxon}.</li>
90 *
91 * <h4>Notes</h4>
92 * <p>
93 * A polytomous key node can be referenced from multiple other nodes. Therefore
94 * a node does not have a single parent. Nevertheless it always belongs to a
95 * main key though it may be referenced also by other key nodes.
96 *
97 *
98 * @author a.mueller
99 * @created 13-Oct-2010
100 *
101 */
102@SuppressWarnings("serial")
103@XmlAccessorType(XmlAccessType.FIELD)
104@XmlType(name = "PolytomousKeyNode", propOrder = { "key", "parent", "children",
105                "sortIndex", "nodeNumber", "statement", "question", "feature", "taxon",
106                "subkey", "otherNode", "modifyingText" })
107@XmlRootElement(name = "FeatureNode")
108@Entity
109@Audited
110public class PolytomousKeyNode extends VersionableEntity implements IMultiLanguageTextHolder {
111        private static final Logger logger = Logger.getLogger(PolytomousKeyNode.class);
112
113        // This is the main key a node belongs to. Although other keys may also
114        // reference
115        // <code>this</code> node, a node usually belongs to a given key.
116        @XmlElement(name = "PolytomousKey")
117        @XmlIDREF
118        @XmlSchemaType(name = "IDREF")
119//      @ManyToOne(fetch = FetchType.LAZY, optional=false)
120//      @JoinColumn(nullable=false)
121//      @NotNull
122//      @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE /*, CascadeType.DELETE_ORPHAN */})
123        @ManyToOne(fetch = FetchType.LAZY)
124        @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE })
125        private PolytomousKey key;
126
127        @XmlElementWrapper(name = "Children")
128        @XmlElement(name = "Child")
129        // @OrderColumn("sortIndex") //JPA 2.0 same as @IndexColumn
130        // @IndexColumn does not work because not every FeatureNode has a parent.
131        // But only NotNull will solve the problem (otherwise
132        // we will need a join table
133        // http://stackoverflow.com/questions/2956171/jpa-2-0-ordercolumn-annotation-in-hibernate-3-5
134        // http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-collection-extratype-indexbidir
135        // see also https://forum.hibernate.org/viewtopic.php?p=2392563
136        // http://opensource.atlassian.com/projects/hibernate/browse/HHH-4390
137        // reading works, but writing doesn't
138        //
139        @IndexColumn(name = "sortIndex", base = 0)
140        @OrderBy("sortIndex")
141        @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
142        @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE })
143        private List<PolytomousKeyNode> children = new ArrayList<PolytomousKeyNode>();
144
145        @XmlElement(name = "Parent")
146        @XmlIDREF
147        @XmlSchemaType(name = "IDREF")
148        @Cascade(CascadeType.SAVE_UPDATE)
149        @ManyToOne(fetch = FetchType.LAZY, targetEntity = PolytomousKeyNode.class)
150        @JoinColumn(name = "parent_id" /*
151                                                                         * , insertable=false, updatable=false,
152                                                                         * nullable=false
153                                                                         */)
154        private PolytomousKeyNode parent;
155
156        // see comment on children @IndexColumn
157        private Integer sortIndex;
158
159        @XmlElement(name = "Statement")
160        @XmlIDREF
161        @XmlSchemaType(name = "IDREF")
162        @ManyToOne(fetch = FetchType.LAZY)
163        @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN })
164        private KeyStatement statement;
165
166        @XmlElement(name = "Question")
167        @XmlIDREF
168        @XmlSchemaType(name = "IDREF")
169        @ManyToOne(fetch = FetchType.LAZY)
170        @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN })
171        private KeyStatement question;
172
173        @XmlElement(name = "Feature")
174        @XmlIDREF
175        @XmlSchemaType(name = "IDREF")
176        @ManyToOne(fetch = FetchType.LAZY)
177        private Feature feature;
178
179        @XmlElement(name = "Taxon")
180        @XmlIDREF
181        @XmlSchemaType(name = "IDREF")
182        @ManyToOne(fetch = FetchType.LAZY)
183        @Cascade(CascadeType.SAVE_UPDATE)
184        private Taxon taxon;
185
186        // Refers to an entire key
187        // <code>this</code> node, a node usually belongs to a given key.
188        @XmlElement(name = "SubKey")
189        @XmlIDREF
190        @XmlSchemaType(name = "IDREF")
191        @ManyToOne(fetch = FetchType.LAZY)
192        @Cascade(CascadeType.SAVE_UPDATE)
193        private PolytomousKey subkey;
194
195        // Refers to an other node within this key or an other key
196        @XmlElement(name = "PolytomousKey")
197        @XmlIDREF
198        @XmlSchemaType(name = "IDREF")
199        @ManyToOne(fetch = FetchType.LAZY)
200        private PolytomousKeyNode otherNode;
201
202        private Integer nodeNumber = 0;
203
204        // TODO should be available for each taxon/result
205        @XmlElement(name = "ModifyingText")
206        @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
207        @OneToMany(fetch = FetchType.LAZY)
208        // @JoinTable(name = "DescriptionElementBase_ModifyingText")
209        @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE })
210        private Map<Language, LanguageString> modifyingText = new HashMap<Language, LanguageString>();
211
212        /**
213         * Class constructor: creates a new empty feature node instance.
214         */
215        protected PolytomousKeyNode() {
216                super();
217        }
218
219        /**
220         * Creates a new empty polytomous key node instance.
221         */
222        public static PolytomousKeyNode NewInstance() {
223                return new PolytomousKeyNode();
224        }
225
226        /**
227         * Creates a new empty polytomous key node instance and sets the node number
228         * to 0.
229         */
230        public static PolytomousKeyNode NewRootInstance() {
231                PolytomousKeyNode result = new PolytomousKeyNode();
232                result.setNodeNumber(0);
233                return result;
234        }
235
236        /**
237         * Creates a new polytomous key node instance.
238         *
239         */
240        public static PolytomousKeyNode NewInstance(String statement) {
241                PolytomousKeyNode result = new PolytomousKeyNode();
242                result.setStatement(KeyStatement.NewInstance(statement));
243                return result;
244        }
245
246        /**
247         * Creates a new polytomous key node instance.
248         *
249         */
250        public static PolytomousKeyNode NewInstance(String statement,
251                        String question, Taxon taxon, Feature feature) {
252                PolytomousKeyNode result = new PolytomousKeyNode();
253                result.setTaxon(taxon);
254                result.setStatement(KeyStatement.NewInstance(statement));
255                result.setQuestion(KeyStatement.NewInstance(question));
256                result.setFeature(feature);
257                return result;
258        }
259
260        // ** ********************** CHILDREN ******************************/
261
262        /**
263         * @return
264         */
265        public PolytomousKey getKey() {
266                return key;
267        }
268
269        /**
270         * @param key
271         */
272        public void setKey(PolytomousKey key) {
273                this.key = key;
274        }
275
276        /**
277         * The node number is the number of the node within the key. This
278         * corresponds to the number for key choices in written keys.
279         */
280        public Integer getNodeNumber() {
281                return nodeNumber;
282        }
283
284        /**
285         * Is computed automatically and therefore should not be set by the user.
286         */
287        private void setNodeNumber(Integer nodeNumber) {
288                this.nodeNumber = nodeNumber;
289        }
290
291        /**
292         * Returns the parent node of <code>this</code> child.
293         *
294         * @return
295         */
296        public PolytomousKeyNode getParent() {
297                return parent;
298        }
299
300        /**
301         * For bidirectional use only !
302         *
303         * @param parent
304         */
305        protected void setParent(PolytomousKeyNode parent) {
306                this.parent = parent;
307        }
308
309        /**
310         * Returns the (ordered) list of feature nodes which are children nodes of
311         * <i>this</i> feature node.
312         */
313        public List<PolytomousKeyNode> getChildren() {
314                return children;
315        }
316
317        /**
318         * Adds the given polytomous key node at the end of the list of children of
319         * <i>this</i> polytomous key node.
320         *
321         * @param child
322         *            the feature node to be added
323         * @see #getChildren()
324         * @see #setChildren(List)
325         * @see #addChild(PolytomousKeyNode, int)
326         * @see #removeChild(PolytomousKeyNode)
327         * @see #removeChild(int)
328         */
329        public void addChild(PolytomousKeyNode child) {
330                addChild(child, children.size());
331        }
332
333        /**
334         * Inserts the given child node in the list of children of <i>this</i>
335         * polytomous key node at the given (index + 1) position. If the given index
336         * is out of bounds an exception will be thrown.<BR>
337         *
338         * @param child
339         *            the polytomous key node to be added
340         * @param index
341         *            the integer indicating the position at which the child should
342         *            be added
343         * @see #getChildren()
344         * @see #setChildren(List)
345         * @see #addChild(PolytomousKeyNode)
346         * @see #removeChild(PolytomousKeyNode)
347         * @see #removeChild(int)
348         */
349        public void addChild(PolytomousKeyNode child, int index) {
350                if (index < 0 || index > children.size() + 1) {
351                        throw new IndexOutOfBoundsException("Wrong index: " + index);
352                }
353
354                children.add(index, child);
355                child.setKey(this.getKey());
356                // TODO workaround (see sortIndex doc)
357                for (int i = 0; i < children.size(); i++) {
358                        children.get(i).sortIndex = i;
359                }
360                child.sortIndex = index;
361                updateNodeNumber();
362                child.setParent(this);
363        }
364
365        private void updateNodeNumber() {
366                int nodeNumber = 0;
367                PolytomousKeyNode root = getKey().getRoot();
368                root.setNodeNumber(nodeNumber++);
369                nodeNumber = updateChildNodeNumbers(nodeNumber, root);
370        }
371
372        private int updateChildNodeNumbers(int nodeNumber, PolytomousKeyNode parent) {
373                if (parent.isLeaf()) {
374                        parent.setNodeNumber(null);
375                } else {
376                        for (PolytomousKeyNode child : parent.getChildren()) {
377                                child.setNodeNumber(nodeNumber++);
378                                nodeNumber = updateChildNodeNumbers(nodeNumber, child);
379                        }
380                }
381                return nodeNumber;
382        }
383
384        /**
385         * Removes the given polytomous key node from the list of
386         * {@link #getChildren() children} of <i>this</i> polytomous key node.
387         *
388         * @param child
389         *            the feature node which should be removed
390         * @see #getChildren()
391         * @see #addChild(PolytomousKeyNode, int)
392         * @see #addChild(PolytomousKeyNode)
393         * @see #removeChild(int)
394         */
395        public void removeChild(PolytomousKeyNode child) {
396                int index = children.indexOf(child);
397                if (index >= 0) {
398                        removeChild(index);
399                }
400        }
401
402        /**
403         * Removes the feature node placed at the given (index + 1) position from
404         * the list of {@link #getChildren() children} of <i>this</i> feature node.
405         * If the given index is out of bounds no child will be removed.
406         *
407         * @param index
408         *            the integer indicating the position of the feature node to be
409         *            removed
410         * @see #getChildren()
411         * @see #addChild(PolytomousKeyNode, int)
412         * @see #addChild(PolytomousKeyNode)
413         * @see #removeChild(PolytomousKeyNode)
414         */
415        public void removeChild(int index) {
416                PolytomousKeyNode child = children.get(index);
417                if (child != null) {
418                        children.remove(index);
419                        // child.setParent(null);
420                        // TODO workaround (see sortIndex doc)
421                        for (int i = 0; i < children.size(); i++) {
422                                PolytomousKeyNode childAt = children.get(i);
423                                childAt.sortIndex = i;
424                        }
425                        child.sortIndex = null;
426                }
427                updateNodeNumber();
428        }
429
430        /**
431         * Returns the feature node placed at the given (childIndex + 1) position
432         * within the list of {@link #getChildren() children} of <i>this</i> feature
433         * node. If the given index is out of bounds no child will be returned.
434         *
435         * @param childIndex
436         *            the integer indicating the position of the feature node
437         * @see #getChildren()
438         * @see #addChild(PolytomousKeyNode, int)
439         * @see #removeChild(int)
440         */
441        public PolytomousKeyNode getChildAt(int childIndex) {
442                return children.get(childIndex);
443        }
444
445        /**
446         * Returns the number of children nodes of <i>this</i> feature node.
447         *
448         * @see #getChildren()
449         */
450        @Transient
451        public int childCount() {
452                return children.size();
453        }
454
455        /**
456         * Returns the integer indicating the position of the given feature node
457         * within the list of {@link #getChildren() children} of <i>this</i> feature
458         * node. If the list does not contain this node then -1 will be returned.
459         *
460         * @param node
461         *            the feature node the position of which is being searched
462         * @see #addChild(PolytomousKeyNode, int)
463         * @see #removeChild(int)
464         */
465        public int getIndex(PolytomousKeyNode node) {
466                if (!children.contains(node)) {
467                        return -1;
468                } else {
469                        return children.indexOf(node);
470                }
471        }
472
473        /**
474         * Returns the boolean value indicating if <i>this</i> feature node has
475         * children (false) or not (true). A node without children is at the
476         * bottommost level of a tree and is called a leaf.
477         *
478         * @see #getChildren()
479         * @see #getChildCount()
480         */
481        @Transient
482        public boolean isLeaf() {
483                return children.size() < 1;
484        }
485
486        // ** ********************** QUESTIONS AND STATEMENTS
487        // ******************************/
488
489        /**
490         * Returns the statement for <code>this</code> PolytomousKeyNode. If the
491         * user agrees with the statement, the node will be followed.
492         *
493         * @return the statement
494         * @see #getQuestion()
495         */
496        public KeyStatement getStatement() {
497                return statement;
498        }
499
500        /**
501         * This is a convenience method to set the statement text for this node in
502         * the given language. <BR>
503         * If no statement exists yet a new statement is created. <BR>
504         * If a statement text in the given language exists already it is
505         * overwritten and the old text is returned. If language is
506         * <code>null</code> the default language is used instead.
507         *
508         * @param text
509         *            the statement text
510         * @param language
511         *            the language of the statement text
512         * @return the old statement text in the given language as LanguageString
513         */
514        public LanguageString addStatementText(String text, Language language) {
515                if (language == null) {
516                        language = Language.DEFAULT();
517                }
518                if (this.statement == null) {
519                        setStatement(KeyStatement.NewInstance());
520                }
521                return getStatement().putLabel(language, text);
522        }
523
524        /**
525         * @param statement
526         * @see #getStatement()
527         */
528        public void setStatement(KeyStatement statement) {
529                this.statement = statement;
530        }
531
532        /**
533         * Returns the question for <code>this</code> PolytomousKeyNode. <BR>
534         * A question is answered by statements in leads below this tree node.
535         * Questions are optional and are usually empty in traditional keys.
536         *
537         * @return the statement
538         * @see #getStatement()
539         */
540        public KeyStatement getQuestion() {
541                return question;
542        }
543
544        /**
545         * This is a convenience method to sets the question text for this node in
546         * the given language. <BR>
547         * If no question exists yet a new question is created. <BR>
548         * If a question text in the given language exists already it is overwritten
549         * and the old text is returned. If language is <code>null</code> the
550         * default language is used instead.
551         *
552         * @param text
553         * @param language
554         * @return
555         */
556        public LanguageString addQuestionText(String text, Language language) {
557                if (language == null) {
558                        language = Language.DEFAULT();
559                }
560                if (this.question == null) {
561                        setQuestion(KeyStatement.NewInstance());
562                }
563                return getQuestion().putLabel(language, text);
564        }
565
566        /**
567         * @param question
568         * @see #getQuestion()
569         */
570        public void setQuestion(KeyStatement question) {
571                this.question = question;
572        }
573
574        // **************** modifying text ***************************************
575
576        /**
577         * Returns the {@link MultilanguageText} like "an unusual form of",
578         * commenting the determined taxon. That is a modifyingText may by used to
579         * comment or to constraint the decision step represented by the edge
580         * leading to <i>this</i> node
581         * <p>
582         * All {@link LanguageString language strings} contained in the
583         * multilanguage texts should all have the same meaning.<BR>
584         */
585        public Map<Language, LanguageString> getModifyingText() {
586                return this.modifyingText;
587        }
588
589        /**
590         * See {@link #getModifyingText}
591         *
592         * @param description
593         *            the language string describing the validity in a particular
594         *            language
595         * @see #getModifyingText()
596         * @see #putModifyingText(Language, String)
597         * @deprecated should follow the put semantic of maps, this method will be
598         *             removed in v4.0 Use the
599         *             {@link #putModifyingText(LanguageString) putModifyingText}
600         *             method instead
601         */
602        @Deprecated
603        public LanguageString addModifyingText(LanguageString description) {
604                return this.putModifyingText(description);
605        }
606
607        /**
608         * See {@link #getModifyingText}
609         *
610         * @param description
611         *            the language string describing the validity in a particular
612         *            language
613         * @see #getModifyingText()
614         * @see #putModifyingText(Language, String)
615         */
616        public LanguageString putModifyingText(LanguageString description) {
617                return this.modifyingText.put(description.getLanguage(), description);
618        }
619
620        /**
621         * See {@link #getModifyingText}
622         *
623         * @param text
624         *            the string describing the validity in a particular language
625         * @param language
626         *            the language in which the text string is formulated
627         * @see #getModifyingText()
628         * @see #putModifyingText(LanguageString)
629         * @deprecated should follow the put semantic of maps, this method will be
630         *             removed in v4.0 Use the
631         *             {@link #putModifyingText(Language, String) putModifyingText}
632         *             method instead
633         */
634        @Deprecated
635        public LanguageString addModifyingText(String text, Language language) {
636                return this.putModifyingText(language, text);
637        }
638
639        /**
640         * See {@link #getModifyingText}
641         *
642         * @param text
643         *            the string describing the validity in a particular language
644         * @param language
645         *            the language in which the text string is formulated
646         * @see #getModifyingText()
647         * @see #putModifyingText(LanguageString)
648         */
649        public LanguageString putModifyingText(Language language, String text) {
650                return this.modifyingText.put(language,
651                                LanguageString.NewInstance(text, language));
652        }
653
654        /**
655         * See {@link #getModifyingText}
656         *
657         * @param language
658         *            the language in which the language string to be removed has
659         *            been formulated
660         * @see #getModifyingText()
661         */
662        public LanguageString removeModifyingText(Language language) {
663                return this.modifyingText.remove(language);
664        }
665
666        /**
667         * Returns the taxon this node links to. This is usually the case when this
668         * node is a leaf.
669         *
670         * @return
671         * @see #setTaxon(Taxon)
672         * @see #getSubkey()
673         * @see #getChildren()
674         * @see #getOtherNode()
675         */
676        public Taxon getTaxon() {
677                return taxon;
678        }
679
680        /**
681         * Sets the taxon this node links to. <BR>
682         * If a tax
683         *
684         * @param taxon
685         * @see #getTaxon()
686         */
687        public void setTaxon(Taxon taxon) {
688                this.taxon = taxon;
689        }
690
691        /**
692         * @return
693         * @see #setSubkey(PolytomousKey)
694         * @see #getTaxon()
695         * @see #getChildren()
696         * @see #getOtherNode()
697         */
698        public PolytomousKey getSubkey() {
699                return subkey;
700        }
701
702        /**
703         * @param subkey
704         * @see #getSubkey()
705         */
706        public void setSubkey(PolytomousKey subkey) {
707                this.subkey = subkey;
708        }
709
710        /**
711         * @return
712         * @see #setOtherNode(PolytomousKeyNode)
713         * @see #getTaxon()
714         * @see #getChildren()
715         * @see #getSubkey()
716         */
717        public PolytomousKeyNode getOtherNode() {
718                return otherNode;
719        }
720
721        /**
722         * @param otherNode
723         * @see #getOtherNode()
724         */
725        public void setOtherNode(PolytomousKeyNode otherNode) {
726                this.otherNode = otherNode;
727        }
728
729        // TODO
730        public void setFeature(Feature feature) {
731                this.feature = feature;
732        }
733
734        public Feature getFeature() {
735                return feature;
736        }
737
738        // *********************** CLONE
739        // ********************************************************/
740
741        /**
742         * Clones <i>this</i> PolytomousKeyNode. This is a shortcut that enables to
743         * create a new instance that differs only slightly from <i>this</i>
744         * PolytomousKeyNode by modifying only some of the attributes. The parent,
745         * the feature and the key are the are the same as for the original feature
746         * node the children are removed.
747         *
748         * @see eu.etaxonomy.cdm.model.common.VersionableEntity#clone()
749         * @see java.lang.Object#clone()
750         */
751        @Override
752        public Object clone() {
753                PolytomousKeyNode result;
754                try {
755                        result = (PolytomousKeyNode) super.clone();
756                        result.children = new ArrayList<PolytomousKeyNode>();
757
758                        result.modifyingText = new HashMap<Language, LanguageString>();
759                        for (Entry<Language, LanguageString> entry : this.modifyingText
760                                        .entrySet()) {
761                                result.putModifyingText(entry.getValue());
762                        }
763
764                        return result;
765                } catch (CloneNotSupportedException e) {
766                        logger.warn("Object does not implement cloneable");
767                        e.printStackTrace();
768                        return null;
769                }
770        }
771
772}
Note: See TracBrowser for help on using the browser.