* The contents of this file are subject to the Mozilla Public License Version 1.1
* See LICENSE.TXT at the top of this package for the full license terms.
*/
-
package eu.etaxonomy.cdm.model.occurrence;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
+import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
+import javax.persistence.Table;
import javax.persistence.Transient;
-import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.apache.log4j.Logger;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
-import org.hibernate.annotations.Index;
-import org.hibernate.annotations.Table;
import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.search.annotations.Analyze;
-import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.FieldBridge;
+import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.IndexedEmbedded;
-import org.hibernate.search.annotations.NumericField;
-
+import org.hibernate.search.annotations.SortableField;
+import org.hibernate.search.annotations.Store;
+import org.hibernate.search.bridge.builtin.BooleanBridge;
+
+import eu.etaxonomy.cdm.common.URI;
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
+import eu.etaxonomy.cdm.hibernate.search.UriBridge;
+import eu.etaxonomy.cdm.jaxb.FormattedTextAdapter;
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
-import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
import eu.etaxonomy.cdm.model.common.IPublishable;
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
import eu.etaxonomy.cdm.model.common.Language;
import eu.etaxonomy.cdm.model.common.LanguageString;
import eu.etaxonomy.cdm.model.common.MultilanguageText;
-import eu.etaxonomy.cdm.model.common.TermType;
import eu.etaxonomy.cdm.model.description.DescriptionBase;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
import eu.etaxonomy.cdm.model.description.IDescribable;
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
import eu.etaxonomy.cdm.model.description.TaxonDescription;
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
+import eu.etaxonomy.cdm.model.term.DefinedTerm;
+import eu.etaxonomy.cdm.model.term.TermType;
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.match.Match;
+import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
+import eu.etaxonomy.cdm.strategy.match.MatchMode;
/**
* type figures are observations with at least a figure object in media
* @author m.doering
- * @created 08-Nov-2007 13:06:41
+ * @since 08-Nov-2007 13:06:41
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SpecimenOrObservationBase", propOrder = {
"recordBasis",
+ "identityCache",
+ "protectedIdentityCache",
"publish",
+ "preferredStableUri",
"sex",
"lifeStage",
"kindOfUnit",
@Entity
@Audited
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
-@Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
-public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableEntity<S>
- implements IMultiLanguageTextHolder, IDescribable<DescriptionBase>, IPublishable {
+@Table(name="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnList = "titleCache"),
+ @Index(name = "specimenOrObservationBaseIdentityCacheIndex", columnList = "identityCache") })
+public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy<?>>
+ extends IdentifiableEntity<S>
+ implements IMultiLanguageTextHolder, IIntextReferenceTarget, IDescribable<DescriptionBase<S>>, IPublishable {
+
private static final long serialVersionUID = 6932680139334408031L;
private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
*
* NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
* as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
- * conventions it would specimenOrObservationType.
+ * conventions it would be specimenOrObservationType.
*
* @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
* @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord
@Column(name="recordBasis")
@NotNull
@Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
- parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
+ parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
)
+ @Audited
private SpecimenOrObservationType recordBasis;
@XmlElement(name = "Description")
@OneToMany(mappedBy="describedSpecimenOrObservation", fetch = FetchType.LAZY)
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
- @ContainedIn
@NotNull
- private Set<DescriptionBase> descriptions = new HashSet<DescriptionBase>();
+ private Set<DescriptionBase<S>> descriptions = new HashSet<>();
@XmlElementWrapper(name = "Determinations")
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
@IndexedEmbedded(depth = 2)
@NotNull
- private Set<DeterminationEvent> determinations = new HashSet<DeterminationEvent>();
+ private Set<DeterminationEvent> determinations = new HashSet<>();
@XmlElement(name = "Sex")
@XmlIDREF
@XmlElement(name = "IndividualCount")
@Field(analyze = Analyze.NO)
- @NumericField
- @Min(0)
- private Integer individualCount;
+ private String individualCount;
+
+ /**
+ * The preferred stable identifier (URI) as discussed in
+ * {@link https://dev.e-taxonomy.eu/redmine/issues/5606}
+ */
+ @XmlElement(name = "PreferredStableUri")
+ @Field(analyze = Analyze.NO)
+ @FieldBridge(impl = UriBridge.class)
+ @Type(type="uriUserType")
+ private URI preferredStableUri;
// the verbatim description of this occurrence. Free text usable when no atomised data is available.
// in conjunction with titleCache which serves as the "citation" string for this object
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
@IndexedEmbedded
@NotNull
- protected Map<Language,LanguageString> definition = new HashMap<Language,LanguageString>();
+ protected Map<Language,LanguageString> definition = new HashMap<>();
// events that created derivedUnits from this unit
@XmlElementWrapper(name = "DerivationEvents")
@ManyToMany(fetch=FetchType.LAZY)
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
@NotNull
- protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
+ protected Set<DerivationEvent> derivationEvents = new HashSet<>();
@XmlAttribute(name = "publish")
+ @Field(analyze = Analyze.NO)
+ @FieldBridge(impl=BooleanBridge.class)
private boolean publish = true;
+ @XmlElement(name = "IdentityCache", required = false)
+ @XmlJavaTypeAdapter(FormattedTextAdapter.class)
+ @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
+// @NotEmpty(groups = Level2.class) // implicitly NotNull
+ @Fields({
+ @Field(store=Store.YES),
+ // If the field is only needed for sorting and nothing else, you may configure it as
+ // un-indexed and un-stored, thus avoid unnecessary index growth.
+ @Field(name = "identityCache__sort", analyze = Analyze.NO, store=Store.NO, index = org.hibernate.search.annotations.Index.NO)
+ })
+ @SortableField(forField = "identityCache__sort")
+ @FieldBridge(impl=StripHtmlBridge.class)
+ private String identityCache;
+
+
+ //if true identityCache will not be automatically generated/updated
+ @XmlElement(name = "ProtectedIdentityCache")
+ private boolean protectedIdentityCache;
+
//********************************** CONSTRUCTOR *********************************/
- //for hibernate use only
- @Deprecated
- protected SpecimenOrObservationBase(){super();}
+ //for hibernate use only
+ @Deprecated
+ protected SpecimenOrObservationBase(){
+ super();
+ }
protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
super();
//************************* GETTER / SETTER ***********************/
- /**
- * @see #recordBasis
- * @return
- */
+ /**@see #recordBasis */
public SpecimenOrObservationType getRecordBasis() {
return recordBasis;
}
+ /**@see #recordBasis */
+ public void setRecordBasis(SpecimenOrObservationType recordBasis) {
+ this.recordBasis = recordBasis;
+ }
+
/**
- * @see #recordBasis
- * @param recordBasis
+ * @return the identityCache
*/
- public void setRecordBasis(SpecimenOrObservationType recordBasis) {
- this.recordBasis = recordBasis;
+ public String getIdentityCache() {
+ return identityCache;
+ }
+ /**
+ * @Deprecated For special use only.
+ * Use {@link #setIdentityCache(String, boolean)} instead
+ */
+ @Deprecated
+ public void setIdentityCache(String identityCache) {
+ this.identityCache = identityCache;
+ }
+
+ public void setIdentityCache(String identityCache, boolean isProtected) {
+ this.protectedIdentityCache = isProtected;
+ setIdentityCache(identityCache);
+ }
+
+ /**
+ * @return the protectedIdentityCache
+ */
+ public boolean isProtectedIdentityCache() {
+ return protectedIdentityCache;
+ }
+ /**
+ * @param protectedIdentityCache the protectedIdentityCache to set
+ */
+ public void setProtectedIdentityCache(boolean protectedIdentityCache) {
+ this.protectedIdentityCache = protectedIdentityCache;
+ }
+
+ /**@see #preferredStableUri */
+ public URI getPreferredStableUri() {
+ return preferredStableUri;
+ }
+ /**@see #preferredStableUri */
+ public void setPreferredStableUri(URI preferredStableUri) {
+ this.preferredStableUri = preferredStableUri;
}
* @return
*/
@Override
- public Set<DescriptionBase> getDescriptions() {
+ public Set<DescriptionBase<S>> getDescriptions() {
if(descriptions == null) {
- this.descriptions = new HashSet<DescriptionBase>();
+ this.descriptions = new HashSet<>();
}
return this.descriptions;
}
*/
@Transient
public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
- Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
- for (DescriptionBase descriptionBase : getDescriptions()){
+ Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
+ for (DescriptionBase<?> descriptionBase : getDescriptions()){
if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
if (includeImageGallery || descriptionBase.isImageGallery() == false){
specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
*/
@Transient
public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
- Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
- for (DescriptionBase descriptionBase : getDescriptions()){
+ Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
+ for (DescriptionBase<?> descriptionBase : getDescriptions()){
if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
if (descriptionBase.isImageGallery() == true){
specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
public Set<DerivationEvent> getDerivationEvents() {
if(derivationEvents == null) {
- this.derivationEvents = new HashSet<DerivationEvent>();
+ this.derivationEvents = new HashSet<>();
}
return this.derivationEvents;
}
public Set<DeterminationEvent> getDeterminations() {
if(determinations == null) {
- this.determinations = new HashSet<DeterminationEvent>();
+ this.determinations = new HashSet<>();
}
return this.determinations;
}
this.kindOfUnit = kindOfUnit;
}
- public Integer getIndividualCount() {
+ public String getIndividualCount() {
return individualCount;
}
- public void setIndividualCount(Integer individualCount) {
+ public void setIndividualCount(String individualCount) {
this.individualCount = individualCount;
}
}
-//******************** CLONE **********************************************/
+ public boolean hasCharacterData() {
+ Set<DescriptionBase<S>> descriptions = this.getDescriptions();
+ for (DescriptionBase<?> descriptionBase : descriptions) {
+ if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
+ SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
+ Set<DescriptionElementBase> elements = specimenDescription.getElements();
+ for (DescriptionElementBase descriptionElementBase : elements) {
+ if (descriptionElementBase.isCharacterData()){
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
- /* (non-Javadoc)
- * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
- * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
- * @see java.lang.Object#clone()
+ /**
+ * Returns a list of all description items which
+ * @return
*/
+ @Transient
+ public Collection<DescriptionElementBase> characterData() {
+ Collection<DescriptionElementBase> states = new ArrayList<>();
+ Set<DescriptionBase<S>> descriptions = this.getDescriptions();
+ for (DescriptionBase<?> descriptionBase : descriptions) {
+ if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
+ SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
+ Set<DescriptionElementBase> elements = specimenDescription.getElements();
+ for (DescriptionElementBase descriptionElementBase : elements) {
+ if(descriptionElementBase.isCharacterData()){
+ states.add(descriptionElementBase);
+ }
+ }
+ }
+ }
+ return states;
+ }
+
+ public Collection<DerivedUnit> collectDerivedUnits(boolean addFullSubTree) {
+ Collection<DerivedUnit> derivedUnits = new ArrayList<>();
+ for (DerivationEvent derivationEvent : getDerivationEvents()) {
+ for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
+ derivedUnits.add(derivative);
+ if(addFullSubTree) {
+ derivedUnits.addAll(derivative.collectDerivedUnits(false));
+ }
+ }
+ }
+ return derivedUnits;
+ }
+
+//******************** CLONE **********************************************/
+
@Override
- public Object clone() throws CloneNotSupportedException {
- SpecimenOrObservationBase result = null;
- result = (SpecimenOrObservationBase)super.clone();
+ public SpecimenOrObservationBase<S> clone() throws CloneNotSupportedException {
+ SpecimenOrObservationBase<S> result = (SpecimenOrObservationBase<S>)super.clone();
//defininion (description, languageString)
- result.definition = new HashMap<Language,LanguageString>();
- for(LanguageString languageString : this.definition.values()) {
- LanguageString newLanguageString = (LanguageString)languageString.clone();
- result.putDefinition(newLanguageString);
- }
+ result.definition = cloneLanguageString(this.definition);
//sex
result.setSex(this.sex);
//life stage
result.setLifeStage(this.lifeStage);
+ result.descriptions = new HashSet<>();
//Descriptions
- for(DescriptionBase description : this.descriptions) {
- result.addDescription(description);
+ for(DescriptionBase<S> description : this.descriptions) {
+ result.addDescription(description.clone());
}
- //DeterminationEvent FIXME should clone() the determination
- // as the relationship is OneToMany
+ result.determinations = new HashSet<>();
for(DeterminationEvent determination : this.determinations) {
- result.addDetermination(determination);
+ result.addDetermination(determination.clone());
}
+ result.derivationEvents = new HashSet<>();
//DerivationEvent
for(DerivationEvent derivationEvent : this.derivationEvents) {
+ //TODO should we clone this?
result.addDerivationEvent(derivationEvent);
}
//no changes to: individualCount
return result;
}
-
-
-}
\ No newline at end of file
+}