Merged in changes from cate-development branch and upgraded to spring 3.0.0.RC1
authorben.clark <ben.clark@localhost>
Wed, 7 Oct 2009 12:06:35 +0000 (12:06 +0000)
committerben.clark <ben.clark@localhost>
Wed, 7 Oct 2009 12:06:35 +0000 (12:06 +0000)
34 files changed:
.gitattributes
cdmlib-model/pom.xml
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/agent/AgentBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/agent/Institution.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/agent/Person.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/agent/Team.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/agent/TeamOrPersonBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/AnnotatableEntity.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/CdmBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/ICdmBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/IdentifiableEntity.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/VersionableEntity.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/MediaKey.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/MultiAccessKey.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/PolytomousKey.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/media/Media.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/media/MediaRepresentation.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/molecular/PhylogeneticTree.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NameTypeDesignation.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/NonViralName.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/Rank.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/SpecimenTypeDesignation.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/TaxonNameBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/occurrence/SpecimenOrObservationBase.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level2.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level3.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/CorrectEpithetsForRank.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NoDuplicateNames.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NullOrNotEmpty.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/CorrectEpithetsForRankValidator.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/NullOrNotEmptyValidator.java [new file with mode: 0644]
cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/StubValidator.java [new file with mode: 0644]
cdmlib-model/src/main/resources/ValidationMessages.properties [new file with mode: 0644]
cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/AgentValidationTest.java [new file with mode: 0644]

index 4a58c05619bab337bdd619605db27180b319b5c5..42949ad86d3850b8df967ddeefab1e1bc3f955df 100644 (file)
@@ -1581,6 +1581,15 @@ cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/INonViralNameParser.
 cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java -text
 cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java -text
 cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/ParserProblem.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level2.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level3.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/CorrectEpithetsForRank.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NoDuplicateNames.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NullOrNotEmpty.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/CorrectEpithetsForRankValidator.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/NullOrNotEmptyValidator.java -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/StubValidator.java -text
+cdmlib-model/src/main/resources/ValidationMessages.properties -text
 cdmlib-model/src/main/resources/log4j.properties -text
 cdmlib-model/src/main/resources/terms/AbsenceTerm.csv -text
 cdmlib-model/src/main/resources/terms/AnnotationType.csv -text
@@ -1643,6 +1652,7 @@ cdmlib-model/src/site/resources/images/uml/package_overview.png -text
 cdmlib-model/src/site/site.xml -text
 cdmlib-model/src/test/java/eu/etaxonomy/cdm/aspectj/PropertyChangeTest.java -text
 cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/DatabaseInitialiser.java -text
+cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/AgentValidationTest.java -text
 cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/InstitutionTypeTest.java -text
 cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/InstitutionalMembershipTest.java -text
 cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/common/AnnotationTest.java -text
index f99f4f34400a8c465ff672efe9ebff93c94ead65..36bfa2ad1082e14dd554c4be6d2e9f0c3572367d 100644 (file)
@@ -1,4 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?><project>
+<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>cdmlib-parent</artifactId>
     <groupId>eu.etaxonomy</groupId>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
-      <artifactId>spring-jdbc</artifactId>
+      <artifactId>org.springframework.jdbc</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
-      <artifactId>spring-test</artifactId>
+      <artifactId>org.springframework.test</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
-      <artifactId>spring-aspects</artifactId>
+      <artifactId>org.springframework.aspects</artifactId>
     </dependency>
     <dependency>
       <groupId>net.sf.opencsv</groupId>
       <artifactId>jaxb-api</artifactId>
       <version>2.1</version>
     </dependency>
+    <dependency>
+         <groupId>jdom</groupId>
+         <artifactId>jdom</artifactId>
+       </dependency>
+    <dependency>
+      <groupId>javax.validation</groupId>
+      <artifactId>validation-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-validator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>  
+    </dependency>
+    <dependency>
+         <groupId>jdom</groupId>
+         <artifactId>jdom</artifactId>
+    </dependency>
   </dependencies>
 </project>
\ No newline at end of file
index 4ee42639226d52d44b7c9c64a414ead4071904ab..882bf79ed7227bf0223bfccb1c5756a57efb8b96 100644 (file)
@@ -15,6 +15,7 @@ import java.util.Set;
 import javax.persistence.Embedded;
 import javax.persistence.Entity;
 import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -63,6 +64,7 @@ public abstract class AgentBase<S extends IIdentifiableEntityCacheStrategy> exte
     @Embedded
     @Merge(MergeMode.MERGE)
     @Match(MatchMode.IGNORE)
+    @NotNull
     private Contact contact;
        
        /** 
@@ -72,6 +74,9 @@ public abstract class AgentBase<S extends IIdentifiableEntityCacheStrategy> exte
         * @see         Contact
         */
        public Contact getContact(){
+               if(contact == null) {
+                       this.contact = new Contact();
+               }
                return this.contact;
        }
        /**
index b8ec6e15e44dd4d52d54987b780a5d007204330e..94664a18d9e7f930f41a633f0e243d4e49b0bc15 100644 (file)
@@ -17,6 +17,8 @@ import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -37,6 +39,7 @@ import org.springframework.beans.factory.annotation.Configurable;
 
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
 import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
 
 /**
  * This class represents public or private institutions.
@@ -70,10 +73,14 @@ public class Institution extends AgentBase<IIdentifiableEntityCacheStrategy<Inst
        
     @XmlElement(name = "Code")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String code;
        
     @XmlElement(name = "Name")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String name;
        
     @XmlElementWrapper(name = "Types")
@@ -81,6 +88,7 @@ public class Institution extends AgentBase<IIdentifiableEntityCacheStrategy<Inst
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
     @ManyToMany(fetch = FetchType.LAZY)
+    @NotNull
        private Set<InstitutionType> types = new HashSet<InstitutionType>();
        
     @XmlElement(name = "IsPartOf")
@@ -115,6 +123,9 @@ public class Institution extends AgentBase<IIdentifiableEntityCacheStrategy<Inst
         * @see     InstitutionType
         */
        public Set<InstitutionType> getTypes(){
+               if(types == null) {
+                       this.types = new HashSet<InstitutionType>();
+               }
                return this.types;
        }
        
index b7f48c914e1d1af62037e030999fbadd3fff6b7e..ec3a9ce17e3fa80176cf1fe8137e6d9c1406857e 100644 (file)
@@ -15,11 +15,15 @@ import java.util.Set;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.OneToMany;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlIDREF;
 import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.log4j.Logger;
@@ -30,12 +34,14 @@ import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.Index;
 import org.hibernate.search.annotations.Indexed;
 import org.hibernate.search.annotations.IndexedEmbedded;
+import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Configurable;
 
 import eu.etaxonomy.cdm.model.common.TimePeriod;
 import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
 import eu.etaxonomy.cdm.strategy.match.Match;
 import eu.etaxonomy.cdm.strategy.match.MatchMode;
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
 
 /**
  * This class represents human beings, living or dead.<BR>
@@ -77,29 +83,39 @@ public class Person extends TeamOrPersonBase<Person>{
 
     @XmlElement(name = "Prefix")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String prefix;
     
     @XmlElement(name = "FirstName")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String firstname;
        
     @XmlElement(name = "LastName")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String lastname;
        
     @XmlElement(name = "Suffix")
     @Field(index=Index.TOKENIZED)
+    @NullOrNotEmpty
+    @Size(max = 255)
        private String suffix;
        
     @XmlElement(name = "Lifespan")
     @IndexedEmbedded
     @Match(value=MatchMode.EQUAL_OR_ONE_NULL)
+    @NotNull
        private TimePeriod lifespan = TimePeriod.NewInstance();
        
     @XmlElementWrapper(name = "InstitutionalMemberships")
     @XmlElement(name = "InstitutionalMembership")
     @OneToMany(fetch=FetchType.LAZY, mappedBy = "person")
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @NotNull
        protected Set<InstitutionalMembership> institutionalMemberships = new HashSet<InstitutionalMembership>();
 
        /** 
@@ -165,6 +181,9 @@ public class Person extends TeamOrPersonBase<Person>{
         * @see     InstitutionalMembership
         */
        public Set<InstitutionalMembership> getInstitutionalMemberships(){
+               if(institutionalMemberships == null) {
+                       this.institutionalMemberships = new HashSet<InstitutionalMembership>();
+               }
                return this.institutionalMemberships;
        }
 
@@ -285,6 +304,9 @@ public class Person extends TeamOrPersonBase<Person>{
         * @see  eu.etaxonomy.cdm.model.common.TimePeriod
         */
        public TimePeriod getLifespan(){
+               if(lifespan == null) {
+                       this.lifespan = TimePeriod.NewInstance(new DateTime(),new DateTime());
+               }
                return this.lifespan;
        }
        /**
index 8a0fe0df7e2ef9630891e0567395a79b5ccd2f96..1877f89682e492c2757b6b804e3c51851200fb0b 100644 (file)
@@ -16,6 +16,7 @@ import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.ManyToMany;
 import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -83,6 +84,7 @@ public class Team extends TeamOrPersonBase<Team> {
        @ManyToMany(fetch = FetchType.LAZY)
        @Cascade(CascadeType.SAVE_UPDATE)
        @Match(MatchMode.MATCH)
+       @NotNull
        private List<Person> teamMembers = new ArrayList<Person>();
        
        
@@ -107,6 +109,9 @@ public class Team extends TeamOrPersonBase<Team> {
         * A person may be a member of several distinct teams. 
         */
        public List<Person> getTeamMembers(){
+               if(teamMembers == null) {
+                       this.teamMembers = new ArrayList<Person>();
+               }
                return this.teamMembers;
        }
        
index 88fba1dce362b2d0316b89db38cbc4363b2c6061..d7e77a11a0466806fece25fab6633d1b4f9c3870 100644 (file)
@@ -10,6 +10,7 @@ package eu.etaxonomy.cdm.model.agent;
 \r
 import javax.persistence.Entity;\r
 import javax.persistence.Transient;\r
+import javax.validation.constraints.Size;\r
 import javax.xml.bind.annotation.XmlAccessType;\r
 import javax.xml.bind.annotation.XmlAccessorType;\r
 import javax.xml.bind.annotation.XmlElement;\r
@@ -21,10 +22,10 @@ import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Field;\r
 import org.hibernate.search.annotations.Index;\r
 import org.hibernate.search.annotations.Indexed;\r
-\r
+import eu.etaxonomy.cdm.common.CdmUtils;\r
 import eu.etaxonomy.cdm.common.CdmUtils;\r
 import eu.etaxonomy.cdm.strategy.cache.agent.INomenclaturalAuthorCacheStrategy;\r
-import eu.etaxonomy.cdm.strategy.match.IMatchable;\r
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;\r
 \r
 \r
 /**\r
@@ -48,6 +49,8 @@ public abstract class TeamOrPersonBase<T extends TeamOrPersonBase<?>> extends Ag
 \r
        @XmlElement(name="NomenclaturalTitle")\r
        @Field(index=Index.TOKENIZED)\r
+       @NullOrNotEmpty\r
+    @Size(max = 255)\r
        protected String nomenclaturalTitle;\r
 \r
        @Transient\r
index 823dafaa11090b7c42d9adbcfcab06dad6ba063c..85afa41e619bade65e1eaeeaaae11687dbe1e5d5 100644 (file)
@@ -15,6 +15,7 @@ import java.util.Set;
 import javax.persistence.FetchType;
 import javax.persistence.MappedSuperclass;
 import javax.persistence.OneToMany;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -48,15 +49,17 @@ public abstract class AnnotatableEntity extends VersionableEntity {
        @XmlElementWrapper(name = "Markers")
        @XmlElement(name = "Marker")
        @OneToMany(fetch=FetchType.LAZY)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        protected Set<Marker> markers = new HashSet<Marker>();
        
        @XmlElementWrapper(name = "Annotations")
        @XmlElement(name = "Annotation")
        @OneToMany(fetch=FetchType.LAZY)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        protected Set<Annotation> annotations = new HashSet<Annotation>();
        
        protected AnnotatableEntity() {
@@ -67,6 +70,9 @@ public abstract class AnnotatableEntity extends VersionableEntity {
        
        
        public Set<Marker> getMarkers(){
+               if(markers == null) {
+                       this.markers = new HashSet<Marker>();
+               }
                return this.markers;
        }
        public void addMarker(Marker marker){
@@ -85,6 +91,9 @@ public abstract class AnnotatableEntity extends VersionableEntity {
 //*************** ANNOTATIONS **********************************************
        
        public Set<Annotation> getAnnotations(){
+               if(annotations == null) {
+                       this.annotations = new HashSet<Annotation>();
+               }
                return this.annotations;
        }
        public void addAnnotation(Annotation annotation){
index fa5cd49667fc05c5a8007ddeb74f5e54a79551ff..0953cf91e9d23d97c3b7bdecf7ca439ab0334a72 100644 (file)
@@ -23,6 +23,8 @@ import javax.persistence.Id;
 import javax.persistence.ManyToOne;
 import javax.persistence.MappedSuperclass;
 import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Min;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
@@ -82,15 +84,18 @@ public abstract class CdmBase implements Serializable, ICdmBase{
        @GeneratedValue(generator = "system-increment")
        @DocumentId
        @Match(MatchMode.IGNORE)
+       @NotNull
+       @Min(0)
        private int id;
     
        @XmlAttribute(required = true)
     @XmlJavaTypeAdapter(UUIDAdapter.class)
     @XmlID
        @Type(type="uuidUserType")
-       @NaturalId
+       @NaturalId // This has the effect of placing a "unique" constraint on the database column
        @Column(length=36)
        @Match(MatchMode.IGNORE)
+       @NotNull
        protected UUID uuid;
        
        @XmlElement (name = "Created", type= String.class)
index e974cc2d81190ca45ac70a7112f93d745a6a4076..4a12f69411a02b8a84aaf8468354cb1e2e986889 100644 (file)
@@ -2,10 +2,15 @@ package eu.etaxonomy.cdm.model.common;
 \r
 import java.util.UUID;\r
 \r
+import javax.validation.GroupSequence;\r
+import javax.validation.groups.Default;\r
+\r
 import org.joda.time.DateTime;\r
 \r
-import eu.etaxonomy.cdm.model.agent.Person;\r
+import eu.etaxonomy.cdm.validation.Level2;\r
+import eu.etaxonomy.cdm.validation.Level3;\r
 \r
+@GroupSequence({Default.class, Level2.class, Level3.class})\r
 public interface ICdmBase {\r
 \r
        /**\r
index 748aa2fcdab0ff3af53bee0c949f36f1fb31ae37..206b5bb69bb261c0a3b7934a4a3d6e0516b00c2b 100644 (file)
@@ -23,6 +23,8 @@ import javax.persistence.FetchType;
 import javax.persistence.MappedSuperclass;
 import javax.persistence.OneToMany;
 import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -38,6 +40,7 @@ import org.hibernate.annotations.IndexColumn;
 import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.FieldBridge;
 import org.hibernate.search.annotations.Fields;
+import org.hibernate.validator.constraints.NotEmpty;
 
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.hibernate.StripHtmlBridge;
@@ -53,6 +56,7 @@ import eu.etaxonomy.cdm.strategy.match.MatchMode;
 import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
 import eu.etaxonomy.cdm.strategy.merge.Merge;
 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
+import eu.etaxonomy.cdm.validation.Level2;
 
 /**
  * Superclass for the primary CDM classes that can be referenced from outside via LSIDs and contain a simple generated title string as a label for human reading.
@@ -100,6 +104,8 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        })
        @FieldBridge(impl=StripHtmlBridge.class)
        @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
+       @NotEmpty(groups = Level2.class) // implictly NotNull
+       @Size(max = 255)
        protected String titleCache;
        
        //if true titleCache will not be automatically generated/updated
@@ -112,6 +118,7 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
        //TODO
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        private Set<Rights> rights = new HashSet<Rights>();
     
     @XmlElementWrapper(name = "Credits")
@@ -121,20 +128,23 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
     //TODO
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        private List<Credit> credits = new ArrayList<Credit>();
        
     @XmlElementWrapper(name = "Extensions")
     @XmlElement(name = "Extension")
     @OneToMany(fetch = FetchType.LAZY)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        private Set<Extension> extensions = new HashSet<Extension>();
        
     @XmlElementWrapper(name = "Sources")
     @XmlElement(name = "OriginalSource")
     @OneToMany(fetch = FetchType.LAZY)         
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        @Merge(MergeMode.ADD_CLONE)
+       @NotNull
        private Set<IdentifiableSource> sources = new HashSet<IdentifiableSource>();
     
     @XmlTransient
@@ -155,7 +165,6 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        };
        addPropertyChangeListener(listener);  
     }
-    
 
        /**
         * By default, we expect most cdm objects to be abstract things 
@@ -165,7 +174,7 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         */
        public byte[] getData() {
                return null;
-       }       
+       }
 
 //******************************** CACHE *****************************************************/        
 
@@ -229,12 +238,13 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        public void setLsid(LSID lsid){
                this.lsid = lsid;
        }
-
-       
        /* (non-Javadoc)
         * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
         */
        public Set<Rights> getRights() {
+               if(rights == null) {
+                       this.rights = new HashSet<Rights>();
+               }
                return this.rights;             
        }
        
@@ -253,6 +263,9 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
 
        
        public List<Credit> getCredits() {
+               if(credits == null) {
+                       this.credits = new ArrayList<Credit>();
+               }
                return this.credits;            
        }
        
@@ -297,6 +310,9 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
         */
        public Set<Extension> getExtensions(){
+               if(extensions == null) {
+                       this.extensions = new HashSet<Extension>();
+               }
                return this.extensions;
        }
 
@@ -342,6 +358,9 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
         */
        public Set<IdentifiableSource> getSources() {
+               if(sources == null) {
+                       this.sources = new HashSet<IdentifiableSource>();
+               }
                return this.sources;            
        }
        
@@ -365,7 +384,6 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
        public void removeSource(IdentifiableSource source) {
                this.sources.remove(source);
        }
-
        
 //******************************** TO STRING *****************************************************/    
        
index 23fd0104042ef865851b711f8cc5d0561ccad1be..b4e880e84ce5b1ca38c13405c0317f56277ec931 100644 (file)
@@ -59,6 +59,7 @@ public abstract class VersionableEntity extends CdmBase {
        @XmlElement(name ="Updated", type = String.class)
        @XmlJavaTypeAdapter(DateTimeAdapter.class)
        //@XmlElement(name ="Updated")
+       //@XmlElement(name ="Updated")
        @Type(type="dateTimeUserType")
        @Basic(fetch = FetchType.LAZY)
        @Match(MatchMode.IGNORE)
index 0d38dcdb4f2d121208f3c309fbcc641d6f447ec4..f9199c89b3b452f946a3fdc9e11ce76afe4df28f 100644 (file)
@@ -17,6 +17,7 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -27,6 +28,8 @@ import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.log4j.Logger;
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
 import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Indexed;
 
@@ -70,6 +73,7 @@ public class MediaKey extends Media implements IIdentificationKey{
        @XmlIDREF
        @XmlSchemaType(name = "IDREF")
        @ManyToMany(fetch = FetchType.LAZY)
+       @NotNull
        private Set<Taxon> coveredTaxa = new HashSet<Taxon>();
        
        @XmlElementWrapper( name = "GeographicalScope")
@@ -78,6 +82,7 @@ public class MediaKey extends Media implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")
        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name="MediaKey_NamedArea")
+       @NotNull
        private Set<NamedArea> geographicalScope = new HashSet<NamedArea>();
        
        @XmlElementWrapper(name = "TaxonomicScope")
@@ -90,6 +95,7 @@ public class MediaKey extends Media implements IIdentificationKey{
                joinColumns=@JoinColumn(name="mediaKey_fk"),
                inverseJoinColumns=@JoinColumn(name="taxon_fk")
        )
+       @NotNull
        private Set<Taxon> taxonomicScope = new HashSet<Taxon>();
        
        @XmlElementWrapper( name = "ScopeRestrictions")
@@ -98,6 +104,7 @@ public class MediaKey extends Media implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")
        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name="MediaKey_Scope")
+       @NotNull
        private Set<Scope> scopeRestrictions = new HashSet<Scope>();
        
        @XmlElementWrapper( name = "KeyRepresentations")
@@ -105,6 +112,8 @@ public class MediaKey extends Media implements IIdentificationKey{
        @XmlIDREF
        @XmlSchemaType(name = "IDREF")
        @ManyToMany(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+       @NotNull
        private Set<Representation> keyRepresentations = new HashSet<Representation>();
        
        /** 
@@ -127,6 +136,9 @@ public class MediaKey extends Media implements IIdentificationKey{
         * <i>this</i> identification key.
         */
        public Set<Taxon> getCoveredTaxa() {
+               if(coveredTaxa == null) {
+                       this.coveredTaxa = new HashSet<Taxon>();
+               }
                return coveredTaxa;
        }
        /**
@@ -164,6 +176,9 @@ public class MediaKey extends Media implements IIdentificationKey{
         * data where <i>this</i> identification key is valid.
         */
        public Set<NamedArea> getGeographicalScope() {
+               if(geographicalScope == null) {
+                       this.geographicalScope = new HashSet<NamedArea>();
+               }
                return geographicalScope;
        }
        
@@ -194,6 +209,9 @@ public class MediaKey extends Media implements IIdentificationKey{
         * scope of <i>this</i> identification key 
         */
        public Set<Taxon> getTaxonomicScope() {
+               if(taxonomicScope == null) {
+                       this.taxonomicScope = new HashSet<Taxon>();
+               }
                return taxonomicScope;
        }
        
@@ -225,6 +243,9 @@ public class MediaKey extends Media implements IIdentificationKey{
         * <i>this</i> identification key 
         */
        public Set<Representation> getKeyRepresentations() {
+               if(keyRepresentations == null) {
+                       this.keyRepresentations = new HashSet<Representation>();
+               }
                return keyRepresentations;
        }
        
@@ -256,6 +277,9 @@ public class MediaKey extends Media implements IIdentificationKey{
         * <i>this</i> identification key 
         */
        public Set<Scope> getScopeRestrictions() {
+               if(scopeRestrictions == null) {
+                       this.scopeRestrictions = new HashSet<Scope>();
+               }
                return scopeRestrictions;
        }
        
index 5667bbca38d3de464ea0328ae5a32b926db19113..1869873fc7aa9d628faf8dd403d13df2e15e73a1 100644 (file)
@@ -18,6 +18,7 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;\r
 import javax.persistence.JoinTable;\r
 import javax.persistence.ManyToMany;\r
+import javax.validation.constraints.NotNull;\r
 import javax.xml.bind.annotation.XmlAccessType;\r
 import javax.xml.bind.annotation.XmlAccessorType;\r
 import javax.xml.bind.annotation.XmlElement;\r
@@ -28,6 +29,8 @@ import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;\r
 \r
 import org.apache.log4j.Logger;\r
+import org.hibernate.annotations.Cascade;\r
+import org.hibernate.annotations.CascadeType;\r
 import org.hibernate.envers.Audited;\r
 import org.hibernate.search.annotations.Indexed;\r
 \r
@@ -68,6 +71,7 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
        @XmlIDREF\r
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
+       @NotNull\r
        private Set<Taxon> coveredTaxa = new HashSet<Taxon>();\r
        \r
        @XmlElementWrapper(name = "TaxonomicScope")\r
@@ -80,6 +84,7 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
                joinColumns=@JoinColumn(name="multiAccessKey_fk"),\r
                inverseJoinColumns=@JoinColumn(name="taxon_fk")\r
        )\r
+       @NotNull\r
        private Set<Taxon> taxonomicScope = new HashSet<Taxon>();\r
        \r
        @XmlElementWrapper( name = "GeographicalScope")\r
@@ -88,6 +93,7 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
        @JoinTable(name="MultiAccessKey_NamedArea")\r
+       @NotNull\r
        private Set<NamedArea> geographicalScope = new HashSet<NamedArea>();\r
        \r
        @XmlElementWrapper( name = "ScopeRestrictions")\r
@@ -96,6 +102,7 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
        @JoinTable(name="MultiAccessKey_Scope")\r
+       @NotNull\r
        private Set<Scope> scopeRestrictions = new HashSet<Scope>();\r
        \r
        /** \r
@@ -117,6 +124,9 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
         * <i>this</i> identification key.\r
         */\r
        public Set<Taxon> getCoveredTaxa() {\r
+               if(coveredTaxa == null) {\r
+                       this.coveredTaxa = new HashSet<Taxon>();\r
+               }\r
                return coveredTaxa;\r
        }\r
        /**\r
@@ -154,6 +164,9 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
         * data where <i>this</i> identification key is valid.\r
         */\r
        public Set<NamedArea> getGeographicalScope() {\r
+               if(geographicalScope == null) {\r
+                       this.geographicalScope = new HashSet<NamedArea>();\r
+               } \r
                return geographicalScope;\r
        }\r
        \r
@@ -184,6 +197,9 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
         * scope of <i>this</i> identification key \r
         */\r
        public Set<Taxon> getTaxonomicScope() {\r
+               if(taxonomicScope == null) {\r
+                       this.taxonomicScope = new HashSet<Taxon>();\r
+               }\r
                return taxonomicScope;\r
        }\r
        \r
@@ -215,6 +231,9 @@ public class MultiAccessKey extends WorkingSet implements IIdentificationKey{
         * <i>this</i> identification key \r
         */\r
        public Set<Scope> getScopeRestrictions() {\r
+               if(scopeRestrictions == null) {\r
+                       this.scopeRestrictions = new HashSet<Scope>();\r
+               }\r
                return scopeRestrictions;\r
        }\r
        \r
index 6c4e8efc909cf709191f02fdce249dd9fe6801f5..82363cdfaf501ec8a115a944d380f8f9158958e6 100644 (file)
@@ -18,6 +18,7 @@ import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;\r
 import javax.persistence.JoinTable;\r
 import javax.persistence.ManyToMany;\r
+import javax.validation.constraints.NotNull;\r
 import javax.xml.bind.annotation.XmlAccessType;\r
 import javax.xml.bind.annotation.XmlAccessorType;\r
 import javax.xml.bind.annotation.XmlElement;\r
@@ -69,6 +70,7 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
        @XmlIDREF\r
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
+       @NotNull\r
        private Set<Taxon> coveredTaxa = new HashSet<Taxon>();\r
        \r
        @XmlElementWrapper(name = "TaxonomicScope")\r
@@ -81,6 +83,7 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
                joinColumns=@JoinColumn(name="polytomousKey_fk"),\r
                inverseJoinColumns=@JoinColumn(name="taxon_fk")\r
        )\r
+       @NotNull\r
        private Set<Taxon> taxonomicScope = new HashSet<Taxon>();\r
        \r
        @XmlElementWrapper( name = "GeographicalScope")\r
@@ -89,6 +92,7 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
        @JoinTable(name="PolytomousKey_NamedArea")\r
+       @NotNull\r
        private Set<NamedArea> geographicalScope = new HashSet<NamedArea>();\r
        \r
        @XmlElementWrapper( name = "ScopeRestrictions")\r
@@ -97,6 +101,7 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
        @XmlSchemaType(name = "IDREF")\r
        @ManyToMany(fetch = FetchType.LAZY)\r
        @JoinTable(name="PolytomousKey_Scope")\r
+       @NotNull\r
        private Set<Scope> scopeRestrictions = new HashSet<Scope>();\r
        \r
        /** \r
@@ -118,6 +123,9 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
         * <i>this</i> identification key.\r
         */\r
        public Set<Taxon> getCoveredTaxa() {\r
+               if(coveredTaxa == null) {\r
+                       this.coveredTaxa = new HashSet<Taxon>();\r
+               }\r
                return coveredTaxa;\r
        }\r
        /**\r
@@ -155,6 +163,9 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
         * data where <i>this</i> identification key is valid.\r
         */\r
        public Set<NamedArea> getGeographicalScope() {\r
+               if(geographicalScope == null) {\r
+                       this.geographicalScope = new HashSet<NamedArea>();\r
+               }\r
                return geographicalScope;\r
        }\r
        \r
@@ -185,6 +196,9 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
         * scope of <i>this</i> identification key \r
         */\r
        public Set<Taxon> getTaxonomicScope() {\r
+               if(taxonomicScope == null) {\r
+                       this.taxonomicScope = new HashSet<Taxon>();\r
+               }\r
                return taxonomicScope;\r
        }\r
        \r
@@ -216,6 +230,9 @@ public class PolytomousKey extends FeatureTree implements IIdentificationKey{
         * <i>this</i> identification key \r
         */\r
        public Set<Scope> getScopeRestrictions() {\r
+               if(scopeRestrictions == null) {\r
+                       this.scopeRestrictions = new HashSet<Scope>();\r
+               }\r
                return scopeRestrictions;\r
        }\r
        \r
index a463425f63426c9f89c0d5dd7d9228b0eb3b6047..06905fa2b56e7733d12a96b4811d1ead252b251d 100644 (file)
@@ -30,6 +30,7 @@ import javax.persistence.InheritanceType;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -47,6 +48,7 @@ import org.hibernate.annotations.Type;
 import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Indexed;
 import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.validator.constraints.NotEmpty;
 import org.joda.time.DateTime;
 
 import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
@@ -55,6 +57,7 @@ import eu.etaxonomy.cdm.model.agent.AgentBase;
 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.validation.Level2;
 
 /**
  * A {@link Media media} is any kind of media that represents a media object. 
@@ -90,7 +93,9 @@ public class Media extends IdentifiableEntity implements Cloneable {
     @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
     @OneToMany(fetch = FetchType.LAZY)
     @IndexedEmbedded
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE})
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+    @NotNull
+    @NotEmpty(groups = Level2.class)
        private Map<Language,LanguageString> title = new HashMap<Language,LanguageString>();
        
        //creation date of the media (not of the record) 
@@ -107,7 +112,8 @@ public class Media extends IdentifiableEntity implements Cloneable {
     @OneToMany(fetch = FetchType.LAZY)
     @IndexedEmbedded
     @JoinTable(name = "Media_Description")
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE})
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE,CascadeType.DELETE_ORPHAN})
+    @NotNull
        private Map<Language,LanguageString> description = new HashMap<Language,LanguageString>();
        
        //A single medium such as a picture can have multiple representations in files. 
@@ -115,7 +121,9 @@ public class Media extends IdentifiableEntity implements Cloneable {
        @XmlElementWrapper(name = "MediaRepresentations")
        @XmlElement(name = "MediaRepresentation")
        @OneToMany(mappedBy="media",fetch = FetchType.LAZY)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+       @NotNull
+       @NotEmpty(groups = Level2.class)
        private Set<MediaRepresentation> representations = new HashSet<MediaRepresentation>();
        
        @XmlElement(name = "Artist")
@@ -143,6 +151,9 @@ public class Media extends IdentifiableEntity implements Cloneable {
        }
 
        public Set<MediaRepresentation> getRepresentations(){
+               if(representations == null) {
+                       this.representations = new HashSet<MediaRepresentation>();
+               }
                return this.representations;
        }
 
@@ -172,6 +183,9 @@ public class Media extends IdentifiableEntity implements Cloneable {
        }
 
        public Map<Language,LanguageString> getTitle(){
+               if(title == null) {
+                       this.title = new HashMap<Language,LanguageString>();
+               }
                return this.title;
        }
        
@@ -192,6 +206,9 @@ public class Media extends IdentifiableEntity implements Cloneable {
        }
 
        public Map<Language,LanguageString> getDescription(){
+               if(this.description == null) {
+                       this.description = new HashMap<Language,LanguageString>();
+               }
                return this.description;
        }
        
index 96c2c709a146589cdd2fd2855bc85e3230b4f1b5..adac6bdbe9f5e74e5c0ac3fbf16f96689e070eb9 100644 (file)
@@ -90,7 +90,7 @@ public class MediaRepresentation extends VersionableEntity implements Cloneable{
     @OneToMany (cascade = {javax.persistence.CascadeType.ALL}, fetch= FetchType.LAZY)
        @IndexColumn(name="sortIndex", base = 0)
        @JoinColumn (name = "representation_id",  nullable=false)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        private List<MediaRepresentationPart> mediaRepresentationParts = new ArrayList<MediaRepresentationPart>();
                
        /**
index 856da5f38bed3fd5d78929b1464fc8fd31369ba3..d1ac9fa0682cc89f55735377532483192052beb8 100644 (file)
@@ -19,6 +19,7 @@ import org.hibernate.search.annotations.Indexed;
 import java.util.*;
 
 import javax.persistence.*;
+import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -49,11 +50,14 @@ public class PhylogeneticTree extends ReferencedMedia {
        @XmlElement(name = "UsedSequence")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
-    @OneToMany(fetch = FetchType.LAZY)
+    @OneToMany(fetch = FetchType.LAZY)// FIXME surely should be ManyToMany - you can use a sequence to construct several different phylogenetic trees
+    @NotNull
        private Set<Sequence> usedSequences = new HashSet<Sequence>();
        
        public Set<Sequence> getUsedSequences() {
-               logger.debug("getUsedSequences");
+               if(usedSequences == null) {
+                       this.usedSequences = new HashSet<Sequence>();
+               }
                return usedSequences;
        }
 
index c66bb042e04459e5ba401643799afde4c2a671c7..f1f76d1e81dc33c575d7e80f289d554ae8ea6354 100644 (file)
@@ -89,6 +89,10 @@ implements ITypeDesignation {
         */
        protected NameTypeDesignation() {
        }
+       
+       public static NameTypeDesignation NewInstance() {
+               return new NameTypeDesignation();
+       }
 
        /**
         * Class constructor: creates a new name type designation instance
@@ -160,7 +164,7 @@ implements ITypeDesignation {
        /**
         * @see  #getTypeName()
         */
-       private void setTypeName(TaxonNameBase typeName){
+       public void setTypeName(TaxonNameBase typeName){
                this.typeName = typeName;
        }
 
index ebe055d062ec1a5f89127abdf9e1fdb8027a62f2..e194033a8e1a737736b6dbc72569702e2ab09fd0 100644 (file)
@@ -21,6 +21,8 @@ import javax.persistence.FetchType;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Transient;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -39,6 +41,7 @@ import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.Fields;
 import org.hibernate.search.annotations.Index;
 import org.hibernate.search.annotations.Indexed;
+import org.hibernate.validator.constraints.NotEmpty;
 import org.springframework.beans.factory.annotation.Configurable;
 
 import eu.etaxonomy.cdm.common.CdmUtils;
@@ -56,6 +59,11 @@ import eu.etaxonomy.cdm.strategy.match.MatchMode;
 import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
 import eu.etaxonomy.cdm.strategy.merge.Merge;
 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
+import eu.etaxonomy.cdm.validation.Level2;
+import eu.etaxonomy.cdm.validation.Level3;
+import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
+import eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames;
 
 /**
  * The taxon name class for all non viral taxa. Parenthetical authorship is derived
@@ -93,13 +101,15 @@ import eu.etaxonomy.cdm.strategy.merge.MergeMode;
     "hybridFormula",
     "monomHybrid",
     "binomHybrid",
-    "trinomHybrid"
+    "trinomHybrid",
 })
 @XmlRootElement(name = "NonViralName")
 @Entity
 @Indexed(index = "eu.etaxonomy.cdm.model.name.TaxonNameBase")
 @Audited
 @Configurable
+@CorrectEpithetsForRank(groups = Level2.class)
+@NoDuplicateNames(groups = Level3.class)
 public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonViralNameCacheStrategy> {
        private static final long serialVersionUID = 4441110073881088033L;
        private static final Logger logger = Logger.getLogger(NonViralName.class);
@@ -110,6 +120,8 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
     })
        @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, 
                        cacheReplacedProperties={"genusOrUninomial", "infraGenericEpithet", "specificEpithet", "infraSpecificEpithet"} )
+       @NotEmpty(groups = Level2.class) // implictly NotNull
+       @Size(max = 255)
        private String nameCache;
        
        @XmlElement(name = "ProtectedNameCache")
@@ -120,22 +132,35 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
        @Field(index=Index.TOKENIZED)
        @Match(MatchMode.EQUAL_REQUIRED)
        @CacheUpdate("nameCache")
-    private String genusOrUninomial;
+       @NullOrNotEmpty
+    @Size(max = 255)
+    @Pattern(regexp = "[A-Z][a-z\\uc3a4\\uc3ab\\uc3af\\uc3b6\\uc3bc\\-]+", groups=Level2.class)
+    @NotEmpty(groups = Level3.class)
+       private String genusOrUninomial;
        
        @XmlElement(name = "InfraGenericEpithet")
        @Field(index=Index.TOKENIZED)
        @CacheUpdate("nameCache")
-    private String infraGenericEpithet;
+       @NullOrNotEmpty
+    @Size(max = 255)
+    @Pattern(regexp = "[a-z\\uc3a4\\uc3ab\\uc3af\\uc3b6\\uc3bc\\-]+", groups=Level2.class)
+       private String infraGenericEpithet;
        
        @XmlElement(name = "SpecificEpithet")
        @Field(index=Index.TOKENIZED)
        @CacheUpdate("nameCache")
-    private String specificEpithet;
+       @NullOrNotEmpty
+    @Size(max = 255)
+    @Pattern(regexp = "[a-z\\uc3a4\\uc3ab\\uc3af\\uc3b6\\uc3bc\\-]+", groups=Level2.class)
+       private String specificEpithet;
        
        @XmlElement(name = "InfraSpecificEpithet")
        @Field(index=Index.TOKENIZED)
        @CacheUpdate("nameCache")
-    private String infraSpecificEpithet;
+       @NullOrNotEmpty
+    @Size(max = 255)
+    @Pattern(regexp = "[a-z\\uc3a4\\uc3ab\\uc3af\\uc3b6\\uc3bc\\-]+", groups=Level3.class)
+       private String infraSpecificEpithet;
        
        @XmlElement(name = "CombinationAuthorTeam", type = TeamOrPersonBase.class)
     @XmlIDREF
@@ -144,7 +169,7 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
        @Target(TeamOrPersonBase.class)
        @Cascade(CascadeType.SAVE_UPDATE)
        @CacheUpdate("authorshipCache")
-    private INomenclaturalAuthor combinationAuthorTeam;
+       private INomenclaturalAuthor combinationAuthorTeam;
        
        @XmlElement(name = "ExCombinationAuthorTeam", type = TeamOrPersonBase.class)
     @XmlIDREF
@@ -153,7 +178,7 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
        @Target(TeamOrPersonBase.class)
        @Cascade(CascadeType.SAVE_UPDATE)
        @CacheUpdate("authorshipCache")
-    private INomenclaturalAuthor exCombinationAuthorTeam;
+       private INomenclaturalAuthor exCombinationAuthorTeam;
        
        @XmlElement(name = "BasionymAuthorTeam", type = TeamOrPersonBase.class)
     @XmlIDREF
@@ -162,7 +187,7 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
        @Target(TeamOrPersonBase.class)
        @Cascade(CascadeType.SAVE_UPDATE)
        @CacheUpdate("authorshipCache")
-    private INomenclaturalAuthor basionymAuthorTeam;
+       private INomenclaturalAuthor basionymAuthorTeam;
        
        @XmlElement(name = "ExBasionymAuthorTeam", type = TeamOrPersonBase.class)
     @XmlIDREF
@@ -171,17 +196,19 @@ public class NonViralName<T extends NonViralName> extends TaxonNameBase<T, INonV
        @Target(TeamOrPersonBase.class)
        @Cascade(CascadeType.SAVE_UPDATE)
        @CacheUpdate("authorshipCache")
-    private INomenclaturalAuthor exBasionymAuthorTeam;
+       private INomenclaturalAuthor exBasionymAuthorTeam;
        
        @XmlElement(name = "AuthorshipCache")
        @Field(index=Index.TOKENIZED)
        @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.DEFINED, 
                        cacheReplacedProperties={"combinationAuthorTeam", "basionymAuthorTeam", "exCombinationAuthorTeam", "exBasionymAuthorTeam"} )
+       @NotEmpty(groups = Level2.class) // implictly NotNull
+       @Size(max = 255)
        private String authorshipCache;
        
        @XmlElement(name = "ProtectedAuthorshipCache")
-       @CacheUpdate(value="authorshipCache")
-    protected boolean protectedAuthorshipCache;
+       @CacheUpdate("authorshipCache")
+       protected boolean protectedAuthorshipCache;
 
     @XmlElementWrapper(name = "HybridRelationsFromThisName")
     @XmlElement(name = "HybridRelationsFromThisName")
index 2a52e0c68ad7399bc8339098f4b3b03f844f1bc7..39d7be0c45e148c3fa6b62570fba5dd633ed08e6 100644 (file)
@@ -838,7 +838,7 @@ public class Rank extends OrderedTermBase<Rank> {
                        return "xxx.";
                }
        }
-       
+       @Transient
        public String getInfraGenericMarker() throws UnknownCdmTypeException{
                if (! this.isInfraGeneric()){
                        throw new IllegalStateException("An infrageneric marker is only available for a infrageneric rank but was asked for rank: " + this.toString());
index a6c580667edf308257f37824c0f9bd9315fe32b4..a9974b837f3f212de3515edc97143ee87ede25f5 100644 (file)
@@ -104,6 +104,10 @@ implements ITypeDesignation {
        protected SpecimenTypeDesignation(){
        }
        
+       public static SpecimenTypeDesignation NewInstance() {
+               return new SpecimenTypeDesignation();
+       }
+       
        /**
         * Class constructor: creates a new specimen type designation instance
         * (including its {@link eu.etaxonomy.cdm.model.reference.ReferenceBase reference source} and 
index 972281f3512176fa75c041594a8fcfa42927bd0b..6812de6ee1297808d5e50415e6c93f4fc3295862 100644 (file)
@@ -24,6 +24,8 @@ import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Transient;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
@@ -41,6 +43,7 @@ import org.hibernate.annotations.Index;
 import org.hibernate.annotations.Table;
 import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Field;
+import org.hibernate.validator.constraints.NotEmpty;
 import org.springframework.util.ReflectionUtils;
 
 import eu.etaxonomy.cdm.model.common.IParsable;
@@ -64,6 +67,8 @@ import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
 import eu.etaxonomy.cdm.strategy.merge.Merge;
 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
 import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
+import eu.etaxonomy.cdm.validation.Level2;
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
 
 /**
  * The upmost (abstract) class for scientific taxon names regardless of any
@@ -112,12 +117,14 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
        @Column(length=330, name="fullTitleCache")
        @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
        @CacheUpdate(noUpdate ="titleCache")
-    protected String fullTitleCache;
+       @NotEmpty(groups = Level2.class)
+       @Size(max = 330)
+       protected String fullTitleCache;
        
        //if true titleCache will not be automatically generated/updated
        @XmlElement(name = "ProtectedFullTitleCache")
        @CacheUpdate(value ="fullTitleCache", noUpdate ="titleCache")
-    private boolean protectedFullTitleCache;
+       private boolean protectedFullTitleCache;
        
     @XmlElementWrapper(name = "Descriptions")
     @XmlElement(name = "Description")
@@ -127,17 +134,21 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
        
     @XmlElement(name = "AppendedPhrase")
     @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
-       @CacheUpdate(value ="nameCache")
-    private String appendedPhrase;
+    @CacheUpdate(value ="nameCache")
+    @NullOrNotEmpty
+    @Size(max = 255)
+       private String appendedPhrase;
        
     @XmlElement(name = "NomenclaturalMicroReference")
     @Field(index= org.hibernate.search.annotations.Index.TOKENIZED)
-       @CacheUpdate(noUpdate ="titleCache")
-    private String nomenclaturalMicroReference;
+    @CacheUpdate(noUpdate ="titleCache")
+    @NullOrNotEmpty
+    @Size(max = 255)
+       private String nomenclaturalMicroReference;
        
     @XmlAttribute
     @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
-   private int parsingProblem = 0;
+       private int parsingProblem = 0;
        
     @XmlAttribute
     @CacheUpdate(noUpdate ={"titleCache","fullTitleCache"})
@@ -152,8 +163,9 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
     @ManyToMany(fetch = FetchType.LAZY)
-       //TODO @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
+       //TODO @Cascade({CascadeType.DELETE_ORPHAN})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+       @NotNull
        private Set<TypeDesignationBase> typeDesignations = new HashSet<TypeDesignationBase>();
 
     @XmlElement(name = "HomotypicalGroup")
@@ -163,13 +175,15 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
        @Match(MatchMode.IGNORE)
        @CacheUpdate(noUpdate ="titleCache")
-    private HomotypicalGroup homotypicalGroup;
+       @NotNull
+       private HomotypicalGroup homotypicalGroup;
 
     @XmlElementWrapper(name = "RelationsFromThisName")
     @XmlElement(name = "RelationFromThisName")
     @OneToMany(mappedBy="relatedFrom", fetch= FetchType.LAZY)
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE_ORPHAN})
        @Merge(MergeMode.RELATION)
+       @NotNull
        private Set<NameRelationship> relationsFromThisName = new HashSet<NameRelationship>();
 
     @XmlElementWrapper(name = "RelationsToThisName")
@@ -179,12 +193,14 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
     @OneToMany(mappedBy="relatedTo", fetch= FetchType.LAZY)
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE , CascadeType.DELETE_ORPHAN })
        @Merge(MergeMode.RELATION)
+       @NotNull
        private Set<NameRelationship> relationsToThisName = new HashSet<NameRelationship>();
 
     @XmlElementWrapper(name = "NomenclaturalStatuses")
     @XmlElement(name = "NomenclaturalStatus")
     @OneToMany(fetch= FetchType.LAZY)
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE,CascadeType.DELETE_ORPHAN})
+       @NotNull
        private Set<NomenclaturalStatus> status = new HashSet<NomenclaturalStatus>();
 
     @XmlElementWrapper(name = "TaxonBases")
@@ -192,6 +208,7 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
     @OneToMany(mappedBy="name", fetch= FetchType.LAZY)
+    @NotNull
        private Set<TaxonBase> taxonBases = new HashSet<TaxonBase>();
     
     @XmlElement(name = "Rank")
@@ -199,7 +216,8 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
        @XmlSchemaType(name = "IDREF")
        @ManyToOne(fetch = FetchType.EAGER)
        @CacheUpdate(value ="nameCache")
-    private Rank rank;
+       @NotNull
+       private Rank rank;
 
        @XmlElement(name = "NomenclaturalReference")
     @XmlIDREF
@@ -207,7 +225,7 @@ public abstract class TaxonNameBase<T extends TaxonNameBase<?,?>, S extends INam
     @ManyToOne(fetch = FetchType.LAZY)
        @Cascade({CascadeType.SAVE_UPDATE})
        @CacheUpdate(noUpdate ="titleCache")
-    private ReferenceBase nomenclaturalReference;
+       private ReferenceBase nomenclaturalReference;
        
 // ************* CONSTRUCTORS *************/   
        /** 
@@ -526,6 +544,9 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see    #addRelationshipFromName(TaxonNameBase, NameRelationshipType, String)
         */
        public Set<NameRelationship> getRelationsFromThisName() {
+               if(relationsFromThisName == null) {
+                       this.relationsFromThisName = new HashSet<NameRelationship>();
+               }
                return relationsFromThisName;
        }
 
@@ -538,6 +559,9 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see    #addRelationshipToName(TaxonNameBase, NameRelationshipType, String)
         */
        public Set<NameRelationship> getRelationsToThisName() {
+               if(relationsToThisName == null) {
+                       this.relationsToThisName = new HashSet<NameRelationship>();
+               }
                return relationsToThisName;
        }
        
@@ -551,6 +575,9 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see     NomenclaturalStatusType
         */
        public Set<NomenclaturalStatus> getStatus() {
+               if(status == null) {
+                       this.status = new HashSet<NomenclaturalStatus>();
+               }
                return status;
        }
 
@@ -888,6 +915,9 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see     SpecimenTypeDesignation
         */
        public Set<TypeDesignationBase> getTypeDesignations() {
+               if(typeDesignations == null) {
+                       this.typeDesignations = new HashSet<TypeDesignationBase>();
+               }
                return typeDesignations;
        }
        
@@ -1092,9 +1122,6 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see #getHomotypicalGroup()
         */
        protected void setHomotypicalGroup(HomotypicalGroup homotypicalGroup) {
-               if (homotypicalGroup == null){
-                       homotypicalGroup = HomotypicalGroup.NewInstance();
-               }
                this.homotypicalGroup = homotypicalGroup;
        }
        
@@ -1166,6 +1193,9 @@ public void addRelationshipToName(TaxonNameBase toName, NameRelationshipType typ
         * @see #getSynonyms()
         */
        public Set<TaxonBase> getTaxonBases() {
+               if(taxonBases == null) {
+                       this.taxonBases = new HashSet<TaxonBase>();
+               }
                return this.taxonBases;
        }
        
index 42a1dfa716e8c13560bc0924f178efac5f41a994..549106f122e3ceb2f4c8f8c29a618dbe93453eb5 100644 (file)
@@ -112,6 +112,7 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
        @XmlElement(name = "Description")
        @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
        @OneToMany(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
        @IndexedEmbedded
        protected Map<Language,LanguageString> description = new HashMap<Language,LanguageString>();
        
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level2.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level2.java
new file mode 100644 (file)
index 0000000..6a3121b
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.etaxonomy.cdm.validation;\r
+\r
+public interface Level2 {\r
+\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level3.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level3.java
new file mode 100644 (file)
index 0000000..44a3189
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.etaxonomy.cdm.validation;\r
+\r
+public interface Level3 {\r
+\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/CorrectEpithetsForRank.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/CorrectEpithetsForRank.java
new file mode 100644 (file)
index 0000000..c2ec4e3
--- /dev/null
@@ -0,0 +1,23 @@
+package eu.etaxonomy.cdm.validation.annotation;\r
+\r
+import static java.lang.annotation.ElementType.TYPE;\r
+import static java.lang.annotation.RetentionPolicy.RUNTIME;\r
+\r
+import java.lang.annotation.Documented;\r
+import java.lang.annotation.Retention;\r
+import java.lang.annotation.Target;\r
+\r
+import javax.validation.Constraint;\r
+import javax.validation.Payload;\r
+\r
+import eu.etaxonomy.cdm.validation.constraint.CorrectEpithetsForRankValidator;\r
+\r
+@Target( { TYPE })\r
+@Retention(RUNTIME)\r
+@Constraint(validatedBy = CorrectEpithetsForRankValidator.class)\r
+@Documented\r
+public @interface CorrectEpithetsForRank {\r
+String message() default "{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.message}";\r
+Class<? extends Payload>[] payload() default {};\r
+Class<?>[] groups() default {};\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NoDuplicateNames.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NoDuplicateNames.java
new file mode 100644 (file)
index 0000000..0581c38
--- /dev/null
@@ -0,0 +1,23 @@
+package eu.etaxonomy.cdm.validation.annotation;\r
+\r
+import static java.lang.annotation.ElementType.TYPE;\r
+import static java.lang.annotation.RetentionPolicy.RUNTIME;\r
+\r
+import java.lang.annotation.Documented;\r
+import java.lang.annotation.Retention;\r
+import java.lang.annotation.Target;\r
+\r
+import javax.validation.Constraint;\r
+import javax.validation.Payload;\r
+\r
+import eu.etaxonomy.cdm.validation.constraint.StubValidator;\r
+\r
+@Target( { TYPE })\r
+@Retention(RUNTIME)\r
+@Constraint(validatedBy = StubValidator.class)\r
+@Documented\r
+public @interface NoDuplicateNames {\r
+String message() default "{eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames.message}";\r
+Class<? extends Payload>[] payload() default {};\r
+Class<?>[] groups() default {};\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NullOrNotEmpty.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/NullOrNotEmpty.java
new file mode 100644 (file)
index 0000000..fec21b7
--- /dev/null
@@ -0,0 +1,21 @@
+package eu.etaxonomy.cdm.validation.annotation;\r
+import static java.lang.annotation.ElementType.*;\r
+import static java.lang.annotation.RetentionPolicy.*;\r
+import java.lang.annotation.Documented;\r
+import java.lang.annotation.Retention;\r
+\r
+import java.lang.annotation.Target;\r
+import javax.validation.Constraint;\r
+import javax.validation.Payload;\r
+\r
+import eu.etaxonomy.cdm.validation.constraint.NullOrNotEmptyValidator;\r
+\r
+@Target( { METHOD, FIELD, ANNOTATION_TYPE })\r
+@Retention(RUNTIME)\r
+@Constraint(validatedBy = NullOrNotEmptyValidator.class)\r
+@Documented\r
+public @interface NullOrNotEmpty {\r
+String message() default "{eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty.message}";\r
+Class<? extends Payload>[] payload() default {};\r
+Class<?>[] groups() default {};\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/CorrectEpithetsForRankValidator.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/CorrectEpithetsForRankValidator.java
new file mode 100644 (file)
index 0000000..e32c8d1
--- /dev/null
@@ -0,0 +1,67 @@
+package eu.etaxonomy.cdm.validation.constraint;\r
+\r
+import javax.validation.ConstraintValidator;\r
+import javax.validation.ConstraintValidatorContext;\r
+\r
+import eu.etaxonomy.cdm.model.name.NonViralName;\r
+import eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank;\r
+\r
+public class CorrectEpithetsForRankValidator implements\r
+               ConstraintValidator<CorrectEpithetsForRank, NonViralName> {\r
+\r
+       public void initialize(CorrectEpithetsForRank correctEpithetsForRank) { }\r
+\r
+       public boolean isValid(NonViralName name, ConstraintValidatorContext constraintContext) {\r
+               boolean valid = true;\r
+               if(name.getRank().isSupraGeneric() || name.getRank().isGenus()) {                               \r
+                       if(name.getInfraGenericEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("infraGenericEpithet").addError();\r
+                       }\r
+                       \r
+                       if(name.getSpecificEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("specificEpithet").addError();\r
+                       } \r
+                       if(name.getInfraSpecificEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("infraSpecificEpithet").addError();\r
+                       }\r
+               } else if(name.getRank().isInfraGeneric()) {\r
+                       if(name.getInfraGenericEpithet() == null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNull}").addSubNode("infraGenericEpithet").addError();\r
+                       }\r
+                               \r
+                       if(name.getSpecificEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("specificEpithet").addError();\r
+                       } \r
+                       if(name.getInfraSpecificEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("infraSpecificEpithet").addError();\r
+                       }\r
+               } else if(name.getRank().isSpecies()) {\r
+                       if(name.getSpecificEpithet() == null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNull}").addSubNode("specificEpithet").addError();\r
+                       }\r
+                               \r
+                       if(name.getInfraSpecificEpithet() != null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull}").addSubNode("infraSpecificEpithet").addError();\r
+                       }\r
+               } else if(name.getRank().isInfraSpecific()) {\r
+                       if(name.getSpecificEpithet() == null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNull}").addSubNode("specificEpithet").addError();\r
+                       }\r
+                       if(name.getInfraSpecificEpithet() == null) {\r
+                               valid = false;\r
+                               constraintContext.buildErrorWithMessageTemplate("{eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNull}").addSubNode("infraSpecificEpithet").addError();\r
+                       }\r
+               }\r
+               \r
+               return valid;           \r
+       }\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/NullOrNotEmptyValidator.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/NullOrNotEmptyValidator.java
new file mode 100644 (file)
index 0000000..4dd797f
--- /dev/null
@@ -0,0 +1,22 @@
+package eu.etaxonomy.cdm.validation.constraint;\r
+\r
+import javax.validation.ConstraintValidator;\r
+import javax.validation.ConstraintValidatorContext;\r
+\r
+import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;\r
+\r
+public class NullOrNotEmptyValidator implements\r
+               ConstraintValidator<NullOrNotEmpty, String> {\r
+\r
+       public void initialize(NullOrNotEmpty nullOrNotEmpty) { }\r
+\r
+       public boolean isValid(String string, ConstraintValidatorContext constraintContext) {\r
+               if(string == null) {\r
+                       return true;\r
+               } else if(string.trim().length() > 0) {\r
+                       return true;\r
+               } else {\r
+                       return false;\r
+               }\r
+       }\r
+}\r
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/StubValidator.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/constraint/StubValidator.java
new file mode 100644 (file)
index 0000000..01d4c24
--- /dev/null
@@ -0,0 +1,26 @@
+package eu.etaxonomy.cdm.validation.constraint;\r
+\r
+import java.lang.annotation.Annotation;\r
+\r
+import javax.validation.ConstraintValidator;\r
+import javax.validation.ConstraintValidatorContext;\r
+\r
+/**\r
+ * Stub validatior for use when a constraint uses cdmlib-services component\r
+ * (and therfore the implementation requires components that are not visible \r
+ * in the cdmlib-model package)\r
+ * \r
+ * To resolve this circular dependency, use this stub as the validator in the\r
+ * annotation, then substitute an implementation using an XML config file.\r
+ *  \r
+ * @author ben.clark\r
+ */\r
+public class StubValidator implements\r
+               ConstraintValidator<Annotation,Object> {\r
+       \r
+       public void initialize(Annotation annotation) { }\r
+\r
+       public boolean isValid(Object obj, ConstraintValidatorContext constraintContext) {\r
+               return true;\r
+       }\r
+}\r
diff --git a/cdmlib-model/src/main/resources/ValidationMessages.properties b/cdmlib-model/src/main/resources/ValidationMessages.properties
new file mode 100644 (file)
index 0000000..8ae0932
--- /dev/null
@@ -0,0 +1,5 @@
+eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty.message=must be either null, or contain at least one non-whitespace character\r
+eu.etaxonomy.cdm.validation.annotation.NoDuplicateNames.message=must not have the same uninomial, genus part, infra-generic epithet, specific epithet, infra-specific epithet, and rank as other names in the database.\r
+eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNotNull=must be empty for a name of this rank. Remove this epithet or change the rank of this name\r
+eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.epithetNull=must not be empty for a name of this rank. Add an epithet or change the rank of this name\r
+eu.etaxonomy.cdm.validation.annotation.CorrectEpithetsForRank.message=must have the epithets required for a name of this rank
\ No newline at end of file
diff --git a/cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/AgentValidationTest.java b/cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/agent/AgentValidationTest.java
new file mode 100644 (file)
index 0000000..e12e88a
--- /dev/null
@@ -0,0 +1,39 @@
+/**\r
+ * \r
+ */\r
+package eu.etaxonomy.cdm.model.agent;\r
+\r
+import java.util.Set;\r
+\r
+import javax.validation.ConstraintViolation;\r
+import javax.validation.Validation;\r
+import javax.validation.Validator;\r
+import javax.validation.ValidatorFactory;\r
+\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
+\r
+/**\r
+ * @author b.clark\r
+ *\r
+ */\r
+public class AgentValidationTest {\r
+\r
+       private static Validator validator;\r
+\r
+       @BeforeClass\r
+       public static void onSetUp() throws Exception {\r
+               ValidatorFactory factory = Validation.buildDefaultValidatorFactory();   \r
+               validator = factory.getValidator();\r
+       }\r
+\r
+       @Test\r
+       public void testNullTitleCache() {\r
+               Person person = Person.NewInstance();\r
+               Set<ConstraintViolation<Person>> constraintViolations = validator.validate(person);\r
+               \r
+               for(ConstraintViolation<Person> constraintViolation : constraintViolations) {\r
+                       System.out.println(constraintViolation.getMessage());\r
+               }\r
+       }\r
+}\r