| 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.common; |
|---|
| 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.Field; |
|---|
| 43 | import org.hibernate.search.annotations.Indexed; |
|---|
| 44 | import org.hibernate.search.annotations.IndexedEmbedded; |
|---|
| 45 | import org.hibernate.validator.constraints.Length; |
|---|
| 46 | |
|---|
| 47 | |
|---|
| 48 | /** |
|---|
| 49 | * A single enumeration must only contain DefinedTerm instances of one kind |
|---|
| 50 | * (this means a subclass of DefinedTerm). |
|---|
| 51 | * @author m.doering |
|---|
| 52 | * @version 1.0 |
|---|
| 53 | * @created 08-Nov-2007 13:06:23 |
|---|
| 54 | */ |
|---|
| 55 | @XmlAccessorType(XmlAccessType.FIELD) |
|---|
| 56 | @XmlType(name = "TermVocabulary", propOrder = { |
|---|
| 57 | "termSourceUri", |
|---|
| 58 | "terms" |
|---|
| 59 | }) |
|---|
| 60 | @XmlRootElement(name = "TermVocabulary") |
|---|
| 61 | @Entity |
|---|
| 62 | @Indexed(index = "eu.etaxonomy.cdm.model.common.TermVocabulary") |
|---|
| 63 | @Audited |
|---|
| 64 | @Inheritance(strategy=InheritanceType.SINGLE_TABLE) |
|---|
| 65 | public class TermVocabulary<T extends DefinedTermBase> extends TermBase implements Iterable<T> { |
|---|
| 66 | private static final long serialVersionUID = 1925052321596648672L; |
|---|
| 67 | private static final Logger logger = Logger.getLogger(TermVocabulary.class); |
|---|
| 68 | |
|---|
| 69 | //The vocabulary source (e.g. ontology) defining the terms to be loaded when a database is created for the first time. |
|---|
| 70 | // Software can go and grap these terms incl labels and description. |
|---|
| 71 | // UUID needed? Further vocs can be setup through our own ontology. |
|---|
| 72 | @XmlElement(name = "TermSourceURI") |
|---|
| 73 | @Field(index=org.hibernate.search.annotations.Index.UN_TOKENIZED) |
|---|
| 74 | @Length(max = 255) |
|---|
| 75 | @Type(type="uriUserType") |
|---|
| 76 | private URI termSourceUri; |
|---|
| 77 | |
|---|
| 78 | |
|---|
| 79 | //TODO Changed |
|---|
| 80 | @XmlElementWrapper(name = "Terms") |
|---|
| 81 | @XmlElement(name = "Term") |
|---|
| 82 | @XmlIDREF |
|---|
| 83 | @XmlSchemaType(name = "IDREF") |
|---|
| 84 | @OneToMany(mappedBy="vocabulary", fetch=FetchType.LAZY, targetEntity = DefinedTermBase.class) |
|---|
| 85 | @Type(type="DefinedTermBase") |
|---|
| 86 | @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE}) |
|---|
| 87 | @IndexedEmbedded(depth = 2) |
|---|
| 88 | protected Set<T> terms = getNewTermSet(); |
|---|
| 89 | |
|---|
| 90 | // ********************************* FACTORY METHODS *****************************************/ |
|---|
| 91 | |
|---|
| 92 | public static TermVocabulary NewInstance(String description, String label, String abbrev, URI termSourceUri){ |
|---|
| 93 | return new TermVocabulary(description, label, abbrev, termSourceUri); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | // ************************* CONSTRUCTOR ************************************************* |
|---|
| 97 | |
|---|
| 98 | protected TermVocabulary() { |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | protected TermVocabulary(String term, String label, String labelAbbrev, URI termSourceUri) { |
|---|
| 102 | super(term, label, labelAbbrev); |
|---|
| 103 | setTermSourceUri(termSourceUri); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | // ******************* METHODS *************************************************/ |
|---|
| 108 | |
|---|
| 109 | public T findTermByUuid(UUID uuid){ |
|---|
| 110 | for(T t : terms) { |
|---|
| 111 | if(t.getUuid().equals(uuid)) { |
|---|
| 112 | return t; |
|---|
| 113 | } |
|---|
| 114 | } |
|---|
| 115 | return null; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | @Transient |
|---|
| 119 | Set<T> getNewTermSet() { |
|---|
| 120 | return new HashSet<T>(); |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | public Set<T> getTerms() { |
|---|
| 124 | return terms; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | public void addTerm(T term) { |
|---|
| 128 | term.setVocabulary(this); |
|---|
| 129 | this.terms.add(term); |
|---|
| 130 | } |
|---|
| 131 | public void removeTerm(T term) { |
|---|
| 132 | this.terms.remove(term); |
|---|
| 133 | term.setVocabulary(null); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | public URI getTermSourceUri() { |
|---|
| 137 | return termSourceUri; |
|---|
| 138 | } |
|---|
| 139 | public void setTermSourceUri(URI vocabularyUri) { |
|---|
| 140 | this.termSourceUri = vocabularyUri; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | |
|---|
| 144 | public Iterator<T> iterator() { |
|---|
| 145 | return terms.iterator(); // OLD: new TermIterator<T>(this.terms); |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | public int size(){ |
|---|
| 149 | return terms.size(); |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | |
|---|
| 153 | /** |
|---|
| 154 | * Returns all terms of this vocabulary sorted by their representation defined by the given language. |
|---|
| 155 | * If such an representation does not exist, the representation of the default language is testing instead for ordering. |
|---|
| 156 | * @param language |
|---|
| 157 | * @return |
|---|
| 158 | */ |
|---|
| 159 | public SortedSet<T> getTermsOrderedByLabels(Language language){ |
|---|
| 160 | TermLanguageComparator<T> comp = new TermLanguageComparator<T>(); |
|---|
| 161 | comp.setCompareLanguage(language); |
|---|
| 162 | |
|---|
| 163 | SortedSet<T> result = new TreeSet<T>(comp); |
|---|
| 164 | result.addAll(getTerms()); |
|---|
| 165 | return result; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | /* (non-Javadoc) |
|---|
| 170 | * @see eu.etaxonomy.cdm.model.common.ILoadableTerm#readCsvLine(java.util.List) |
|---|
| 171 | */ |
|---|
| 172 | public TermVocabulary<T> readCsvLine(List<String> csvLine) { |
|---|
| 173 | return readCsvLine(csvLine, Language.CSV_LANGUAGE()); |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | public TermVocabulary<T> readCsvLine(List<String> csvLine, Language lang) { |
|---|
| 177 | this.setUuid(UUID.fromString(csvLine.get(0))); |
|---|
| 178 | this.setUri(URI.create(csvLine.get(1))); |
|---|
| 179 | //this.addRepresentation(Representation.NewInstance(csvLine.get(3), csvLine.get(2).trim(), lang) ); |
|---|
| 180 | return this; |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | //*********************** CLONE ********************************************************/ |
|---|
| 184 | |
|---|
| 185 | /** |
|---|
| 186 | * Clones <i>this</i> TermVocabulary. This is a shortcut that enables to create |
|---|
| 187 | * a new instance that differs only slightly from <i>this</i> TermVocabulary. |
|---|
| 188 | * The terms of the original vocabulary are cloned |
|---|
| 189 | * |
|---|
| 190 | * @see eu.etaxonomy.cdm.model.common.TermBase#clone() |
|---|
| 191 | * @see java.lang.Object#clone() |
|---|
| 192 | */ |
|---|
| 193 | @Override |
|---|
| 194 | public Object clone() { |
|---|
| 195 | TermVocabulary result; |
|---|
| 196 | try { |
|---|
| 197 | result = (TermVocabulary) super.clone(); |
|---|
| 198 | |
|---|
| 199 | }catch (CloneNotSupportedException e) { |
|---|
| 200 | logger.warn("Object does not implement cloneable"); |
|---|
| 201 | e.printStackTrace(); |
|---|
| 202 | return null; |
|---|
| 203 | } |
|---|
| 204 | result.terms = new HashSet<T>(); |
|---|
| 205 | for (T term: this.terms){ |
|---|
| 206 | result.addTerm((T)term.clone()); |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | return result; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | } |
|---|