Project

General

Profile

Download (10.2 KB) Statistics
| Branch: | Tag: | Revision:
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

    
10
package eu.etaxonomy.cdm.model.term;
11

    
12
import java.rmi.UnexpectedException;
13
import java.util.ArrayList;
14
import java.util.HashSet;
15
import java.util.List;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import javax.persistence.Column;
20
import javax.persistence.Entity;
21
import javax.persistence.FetchType;
22
import javax.persistence.OneToMany;
23
import javax.persistence.OneToOne;
24
import javax.persistence.Transient;
25
import javax.validation.constraints.NotNull;
26
import javax.xml.bind.annotation.XmlAccessType;
27
import javax.xml.bind.annotation.XmlAccessorType;
28
import javax.xml.bind.annotation.XmlAttribute;
29
import javax.xml.bind.annotation.XmlElement;
30
import javax.xml.bind.annotation.XmlElementWrapper;
31
import javax.xml.bind.annotation.XmlRootElement;
32
import javax.xml.bind.annotation.XmlType;
33

    
34
import org.apache.log4j.Logger;
35
import org.hibernate.annotations.Cascade;
36
import org.hibernate.annotations.CascadeType;
37
import org.hibernate.annotations.Type;
38
import org.hibernate.envers.Audited;
39

    
40
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
41
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
42
import eu.etaxonomy.cdm.model.description.Feature;
43
import eu.etaxonomy.cdm.model.description.MediaKey;
44
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
45

    
46
/**
47
 * The class to arrange {@link Feature features} (characters) in a tree structure.
48
 * Feature trees are essential as interactive multiple-access keys for
49
 * determination process and for systematical output arrangement of
50
 * {@link DescriptionElementBase description elements} according to different goals
51
 * but may also be used to define flat feature subsets for filtering purposes.<BR>
52
 * A feature tree is build on {@link FeatureNode feature nodes}.
53
 * <P>
54
 * This class corresponds partially to ConceptTreeDefType according to the SDD
55
 * schema.
56
 * <P>
57
 * Note: The tree structure of features used for purposes described above has
58
 * nothing in common with the possible hierarchical structure of features
59
 * depending on their grade of precision.
60
 *
61
 * @see		MediaKey
62
 *
63
 * @author  m.doering
64
 * @since 08-Nov-2007 13:06:16
65
 */
66
@XmlAccessorType(XmlAccessType.FIELD)
67
@XmlType(name = "FeatureTree", propOrder = {
68
    "root",
69
    "termType",
70
    "allowDuplicates",
71
    "representations"
72

    
73
})
74
@XmlRootElement(name = "FeatureTree")
75
@Entity
76
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
77
//@Indexed(index = "eu.etaxonomy.cdm.model.term.FeatureTree")
78
@Audited
79
public class FeatureTree <T extends DefinedTermBase>
80
            extends IdentifiableEntity<IIdentifiableEntityCacheStrategy>
81
            implements IHasTermType, Cloneable{
82

    
83
	private static final long serialVersionUID = -6713834139003172735L;
84
	private static final Logger logger = Logger.getLogger(FeatureTree.class);
85

    
86
	@XmlElement(name = "Root")
87
	@OneToOne(fetch = FetchType.LAZY, targetEntity=FeatureNode.class)
88
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
89
	private FeatureNode<T> root;
90

    
91
    /**
92
     * The {@link TermType type} of this term collection. All nodes in the graph must refer to a term of the same type.
93
     */
94
    @XmlAttribute(name ="TermType")
95
    @Column(name="termType")
96
    @NotNull
97
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
98
        parameters = {@org.hibernate.annotations.Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.term.TermType")}
99
    )
100
    @Audited
101
    private TermType termType;
102

    
103
    // TODO needed? FeatureTree was a TermBase until v3.3 but was removed from
104
	//it as TermBase got the termType which does not apply to FeatureTree.
105
	//We need to check how far representations and uri is still required
106
	//or can be deleted. Current implementations seem all to use the title cache
107
	//instead of representation. This may not be correct.
108
	@XmlElementWrapper(name = "Representations")
109
    @XmlElement(name = "Representation")
110
    @OneToMany(fetch=FetchType.EAGER, orphanRemoval=true)
111
    @Cascade( { CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
112
    // @IndexedEmbedded no need for embedding since we are using the DefinedTermBaseClassBridge
113
    private Set<Representation> representations = new HashSet<>();
114
    //make them private for now as we may delete representations in future
115
	//otherwise if we decide to use representations we can make the getters public
116
	private Set<Representation> getRepresentations() {return representations;}
117
    private void setRepresentations(Set<Representation> representations) {this.representations = representations;}
118

    
119
    //#7372 indicates if this tree/graph allows duplicated terms/features
120
    private boolean allowDuplicates = false;
121

    
122
//******************** FACTORY METHODS ******************************************/
123

    
124
    /**
125
     * Creates a new term collection instance for the given term type
126
     * with an empty {@link #getRoot() root node}.
127
     * @param termType the {@link TermType term type}, must not be null
128
     */
129
    public static <T extends DefinedTermBase<T>> FeatureTree<T> NewInstance(@NotNull TermType termType){
130
        return new FeatureTree<>(termType);
131
    }
132

    
133
    /**
134
	 * Creates a new feature tree instance with an empty {@link #getRoot() root node}.
135
	 *
136
	 * @see #NewInstance(UUID)
137
	 * @see #NewInstance(List)
138
	 */
139
	public static FeatureTree<Feature> NewInstance(){
140
		return new FeatureTree<>(TermType.Feature);
141
	}
142

    
143
	/**
144
	 * Creates a new feature tree instance with an empty {@link #getRoot() root node}
145
	 * and assigns to the new feature tree the given
146
	 * UUID (universally unique identifier).
147
	 *
148
	 * @param	uuid	the universally unique identifier
149
	 * @see 			#NewInstance()
150
	 * @see 			#NewInstance(List)
151
	 */
152
	public static <T extends DefinedTermBase<T>> FeatureTree<T> NewInstance(UUID uuid){
153
		FeatureTree<T> result =  new FeatureTree<>(TermType.Feature);
154
		result.setUuid(uuid);
155
		return result;
156
	}
157

    
158
	/**
159
	 * Creates a new feature tree instance with a {@link #getRoot() root node}
160
	 * the children of which are the feature nodes build on the base of the
161
	 * given list of {@link Feature features}. This corresponds to a flat feature tree.
162
	 * For each feature within the list a new {@link FeatureNode feature node} without
163
	 * children nodes will be created.
164
	 *
165
	 * @param	featureList	the feature list
166
	 * @see 				#NewInstance()
167
	 * @see 				#NewInstance(UUID)
168
	 */
169
	public static FeatureTree<Feature> NewInstance(List<Feature> featureList){
170
		FeatureTree<Feature> result =  new FeatureTree<>(TermType.Feature);
171
		FeatureNode<Feature> root = result.getRoot();
172

    
173
		for (Feature feature : featureList){
174
			FeatureNode<Feature> child = FeatureNode.NewInstance(feature);
175
			root.addChild(child);
176
		}
177

    
178
		return result;
179
	}
180

    
181

    
182
// ******************** CONSTRUCTOR *************************************/
183

    
184
    //for JAXB only, TODO needed?
185
    @Deprecated
186
    protected FeatureTree(){}
187

    
188
	/**
189
	 * Class constructor: creates a new feature tree instance with an empty
190
	 * {@link #getRoot() root node}.
191
	 */
192
	protected FeatureTree(TermType termType) {
193
        this.termType = termType;
194
        checkTermType(this);
195
		root = FeatureNode.NewInstance(termType);
196
		root.setFeatureTree(this);
197
	}
198

    
199
// ****************** GETTER / SETTER **********************************/
200

    
201
	@Override
202
    public TermType getTermType() {
203
        return termType;
204
    }
205
    /**
206
	 * Returns the topmost {@link FeatureNode feature node} (root node) of <i>this</i>
207
	 * feature tree. The root node does not have any parent. Since feature nodes
208
	 * recursively point to their child nodes the complete feature tree is
209
	 * defined by its root node.
210
	 */
211
	public FeatureNode<T> getRoot() {
212
		return root;
213
	}
214

    
215
    /**
216
     * @deprecated this method is only for internal use when deleting a {@link FeatureTree}
217
     * from a database. It should never be called for other reasons.
218
     */
219
    @Deprecated
220
    public void removeRootNode() {
221
        this.root = null;
222
    }
223

    
224
	/**
225
	 * Returns the (ordered) list of {@link FeatureNode feature nodes} which are immediate
226
	 * children of the root node of <i>this</i> feature tree.
227
	 */
228
	@Transient
229
	public List<FeatureNode<T>> getRootChildren(){
230
		List<FeatureNode<T>> result = new ArrayList<>();
231
		result.addAll(root.getChildNodes());
232
		return result;
233
	}
234

    
235
    public boolean isAllowDuplicates() {
236
        return allowDuplicates;
237
    }
238
    public void setAllowDuplicates(boolean allowDuplicates) {
239
        this.allowDuplicates = allowDuplicates;
240
    }
241

    
242
    /**
243
     * Throws {@link IllegalArgumentException} if the given
244
     * term has not the same term type as this term or if term type is null.
245
     * @param term
246
     */
247
    private void checkTermType(IHasTermType term) {
248
        IHasTermType.checkTermTypes(term, this);
249
    }
250

    
251
//******************** METHODS ***********************************************/
252

    
253
	/**
254
	 * Computes a set of distinct terms that are present in this feature tree
255
	 *
256
	 * @return
257
	 */
258
	@Transient
259
	public Set<T> getDistinctFeatures(){
260
	    if(termType.equals(TermType.Feature) || termType.isKindOf(TermType.Feature)){
261
	        Set<T> features = new HashSet<>();
262
	        return root.getDistinctFeaturesRecursive(features);
263
	    }
264
	    String message = "FeatureTree is not of type FEATURE.";
265
	    logger.warn(message, new UnexpectedException(message));
266
	    return new HashSet<>();
267
	}
268

    
269
//*********************** CLONE ********************************************************/
270

    
271
	/**
272
	 * Clones <i>this</i> FeatureTree. This is a shortcut that enables to create
273
	 * a new instance that differs only slightly from <i>this</i> FeatureTree by
274
	 * modifying only some of the attributes.
275
	 * FeatureNodes always belong only to one tree, so all FeatureNodes are cloned to build
276
	 * the new FeatureTree
277
	 *
278
	 *
279
	 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
280
	 * @see java.lang.Object#clone()
281
	 */
282
	@Override
283
	public Object clone() {
284
		FeatureTree<T> result;
285
		try {
286
			result = (FeatureTree<T>)super.clone();
287
		}catch (CloneNotSupportedException e) {
288
			logger.warn("Object does not implement cloneable");
289
			e.printStackTrace();
290
			return null;
291
		}
292
		FeatureNode<T> rootClone = this.getRoot().cloneDescendants();
293
		result.root = rootClone;
294

    
295
		return result;
296

    
297
	}
298

    
299

    
300
}
(6-6/25)