ref #8162 adapt cdmlip to new term package structure
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / term / TermVocabulary.java
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
13 import java.net.URI;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Set;
18 import java.util.SortedSet;
19 import java.util.TreeSet;
20 import java.util.UUID;
21
22 import javax.persistence.Entity;
23 import javax.persistence.FetchType;
24 import javax.persistence.Inheritance;
25 import javax.persistence.InheritanceType;
26 import javax.persistence.OneToMany;
27 import javax.persistence.Transient;
28 import javax.xml.bind.annotation.XmlAccessType;
29 import javax.xml.bind.annotation.XmlAccessorType;
30 import javax.xml.bind.annotation.XmlElement;
31 import javax.xml.bind.annotation.XmlElementWrapper;
32 import javax.xml.bind.annotation.XmlIDREF;
33 import javax.xml.bind.annotation.XmlRootElement;
34 import javax.xml.bind.annotation.XmlSchemaType;
35 import javax.xml.bind.annotation.XmlType;
36
37 import org.apache.log4j.Logger;
38 import org.hibernate.annotations.Cascade;
39 import org.hibernate.annotations.CascadeType;
40 import org.hibernate.annotations.Type;
41 import org.hibernate.envers.Audited;
42 import org.hibernate.search.annotations.Analyze;
43 import org.hibernate.search.annotations.Field;
44 import org.hibernate.search.annotations.IndexedEmbedded;
45
46 import eu.etaxonomy.cdm.common.CdmUtils;
47 import eu.etaxonomy.cdm.model.common.Language;
48
49
50 /**
51 * A single enumeration must only contain DefinedTerm instances of one kind
52 * (this means a subclass of DefinedTerm).
53 * @author m.doering
54 * @since 08-Nov-2007 13:06:23
55 */
56 @XmlAccessorType(XmlAccessType.FIELD)
57 @XmlType(name = "TermVocabulary", propOrder = {
58 "termSourceUri",
59 "terms"
60 })
61 @XmlRootElement(name = "TermVocabulary")
62 @Entity
63 //@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
64 //@Indexed(index = "eu.etaxonomy.cdm.model.common.TermVocabulary")
65 @Audited
66 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
67 public class TermVocabulary<T extends DefinedTermBase> extends TermBase implements Iterable<T> {
68 private static final long serialVersionUID = 1925052321596648672L;
69 private static final Logger logger = Logger.getLogger(TermVocabulary.class);
70
71 //The vocabulary source (e.g. ontology) defining the terms to be loaded when a database is created for the first time.
72 // Software can go and grap these terms incl labels and description.
73 // UUID needed? Further vocs can be setup through our own ontology.
74 @XmlElement(name = "TermSourceURI")
75 @Type(type="uriUserType")
76 @Field(analyze = Analyze.NO)
77 private URI termSourceUri;
78
79
80 //TODO Changed
81 @XmlElementWrapper(name = "Terms")
82 @XmlElement(name = "Term")
83 @XmlIDREF
84 @XmlSchemaType(name = "IDREF")
85 @OneToMany(mappedBy="vocabulary", fetch=FetchType.LAZY, targetEntity = DefinedTermBase.class)
86 @Type(type="DefinedTermBase")
87 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
88 @IndexedEmbedded(depth = 2)
89 protected Set<T> terms = getNewTermSet();
90
91 // ********************************* FACTORY METHODS *****************************************/
92
93
94 public static TermVocabulary NewInstance(TermType type){
95 return new TermVocabulary(type);
96 }
97
98 public static <T extends DefinedTermBase<T>> TermVocabulary<T> NewInstance(TermType type, Class<T> clazz){
99 return new TermVocabulary<T>(type);
100 }
101
102 public static TermVocabulary NewInstance(TermType type, String description, String label, String abbrev, URI termSourceUri){
103 return new TermVocabulary(type, description, label, abbrev, termSourceUri);
104 }
105
106 // ************************* CONSTRUCTOR *************************************************
107
108 //for hibernate use only
109 @Deprecated
110 protected TermVocabulary() {
111 super(TermType.Unknown);
112 }
113
114 protected TermVocabulary(TermType type) {
115 super(type);
116 }
117
118 protected TermVocabulary(TermType type, String term, String label, String labelAbbrev, URI termSourceUri) {
119 super(type, term, label, labelAbbrev);
120 setTermSourceUri(termSourceUri);
121 }
122
123
124 // ******************* METHODS *************************************************/
125
126 public T findTermByUuid(UUID uuid){
127 for(T t : terms) {
128 if(t.getUuid().equals(uuid)) {
129 return t;
130 }
131 }
132 return null;
133 }
134
135 @Transient
136 Set<T> getNewTermSet() {
137 return new HashSet<T>();
138 }
139
140 public Set<T> getTerms() {
141 return terms;
142 }
143
144 public void addTerm(T term) {
145 checkTermType(term);
146 term.setVocabulary(this);
147 this.terms.add(term);
148 }
149 public void removeTerm(T term) {
150 this.terms.remove(term);
151 term.setVocabulary(null);
152 }
153
154 public URI getTermSourceUri() {
155 return termSourceUri;
156 }
157 public void setTermSourceUri(URI vocabularyUri) {
158 this.termSourceUri = vocabularyUri;
159 }
160
161 /**
162 * Returns the first term found having the defined idInVocabulary.
163 * If number of terms with given idInVoc > 1 the result is not deterministic.
164 * @param idInVoc
165 * @return the term with the given idInVoc
166 */
167 public T getTermByIdInvocabulary(String idInVoc) {
168 for (T term : getTerms() ){
169 if (CdmUtils.nullSafeEqual(idInVoc, term.getIdInVocabulary())){
170 return term;
171 }
172 }
173 return null;
174 }
175
176 @Override
177 public Iterator<T> iterator() {
178 return terms.iterator(); // OLD: new TermIterator<T>(this.terms);
179 }
180
181 public int size(){
182 return terms.size();
183 }
184
185
186 /**
187 * Returns all terms of this vocabulary sorted by their representation defined by the given language.
188 * If such an representation does not exist, the representation of the default language is testing instead for ordering.
189 * @param language
190 * @return
191 */
192 public SortedSet<T> getTermsOrderedByLabels(Language language){
193 TermLanguageComparator<T> comp = new TermLanguageComparator<T>();
194 comp.setCompareLanguage(language);
195
196 SortedSet<T> result = new TreeSet<T>(comp);
197 result.addAll(getTerms());
198 return result;
199 }
200
201
202 public TermVocabulary<T> readCsvLine(List<String> csvLine) {
203 return readCsvLine(csvLine, Language.CSV_LANGUAGE());
204 }
205
206 public TermVocabulary<T> readCsvLine(List<String> csvLine, Language lang) {
207 this.setUuid(UUID.fromString(csvLine.get(0)));
208 String uriStr = CdmUtils.Ne(csvLine.get(1));
209 this.setUri(uriStr == null? null: URI.create(uriStr));
210 String label = csvLine.get(2).trim();
211 String description = csvLine.get(3);
212
213 //see http://dev.e-taxonomy.eu/trac/ticket/3550
214 this.addRepresentation(Representation.NewInstance(description, label, null, lang) );
215
216 TermType termType = TermType.getByKey(csvLine.get(4));
217 if (termType == null){
218 throw new IllegalArgumentException("TermType can not be mapped: " + csvLine.get(4));
219 }
220 this.setTermType(termType);
221
222 return this;
223 }
224
225 /**
226 * Throws {@link IllegalArgumentException} if the given
227 * term has not the same term type as this term or if term type is null.
228 * @param term
229 */
230 private void checkTermType(IHasTermType term) {
231 IHasTermType.checkTermTypes(term, this);
232 }
233
234 //*********************** CLONE ********************************************************/
235
236 /**
237 * Clones <i>this</i> TermVocabulary. This is a shortcut that enables to create
238 * a new instance that differs only slightly from <i>this</i> TermVocabulary.
239 * The terms of the original vocabulary are cloned
240 *
241 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
242 * @see java.lang.Object#clone()
243 */
244 @Override
245 public Object clone() {
246 TermVocabulary<T> result;
247 try {
248 result = (TermVocabulary<T>) super.clone();
249
250 }catch (CloneNotSupportedException e) {
251 logger.warn("Object does not implement cloneable");
252 e.printStackTrace();
253 return null;
254 }
255 result.terms = new HashSet<T>();
256 for (T term: this.terms){
257 result.addTerm((T)term.clone());
258 }
259
260 return result;
261 }
262 }