001/*
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.entity;
021
022import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
023import ca.uhn.fhir.jpa.model.entity.IdAndPartitionId;
024import ca.uhn.fhir.jpa.model.entity.ResourceTable;
025import ca.uhn.fhir.util.ValidateUtil;
026import jakarta.annotation.Nonnull;
027import jakarta.persistence.Column;
028import jakarta.persistence.Entity;
029import jakarta.persistence.EnumType;
030import jakarta.persistence.Enumerated;
031import jakarta.persistence.FetchType;
032import jakarta.persistence.ForeignKey;
033import jakarta.persistence.GeneratedValue;
034import jakarta.persistence.GenerationType;
035import jakarta.persistence.Id;
036import jakarta.persistence.IdClass;
037import jakarta.persistence.Index;
038import jakarta.persistence.JoinColumn;
039import jakarta.persistence.JoinColumns;
040import jakarta.persistence.ManyToOne;
041import jakarta.persistence.OneToMany;
042import jakarta.persistence.SequenceGenerator;
043import jakarta.persistence.Table;
044import jakarta.persistence.Temporal;
045import jakarta.persistence.TemporalType;
046import jakarta.persistence.Transient;
047import jakarta.persistence.UniqueConstraint;
048import org.apache.commons.lang3.builder.EqualsBuilder;
049import org.apache.commons.lang3.builder.ToStringBuilder;
050import org.apache.commons.lang3.builder.ToStringStyle;
051import org.hibernate.annotations.ColumnDefault;
052import org.hibernate.annotations.JdbcTypeCode;
053import org.hibernate.type.SqlTypes;
054
055import java.io.Serializable;
056import java.util.ArrayList;
057import java.util.Date;
058import java.util.List;
059
060import static org.apache.commons.lang3.StringUtils.left;
061import static org.apache.commons.lang3.StringUtils.length;
062
063@Table(
064                name = "TRM_VALUESET",
065                uniqueConstraints = {
066                        @UniqueConstraint(
067                                        name = "IDX_VALUESET_URL",
068                                        columnNames = {"PARTITION_ID", "URL", "VER"})
069                },
070                indexes = {
071                        // must have same name that indexed FK or SchemaMigrationTest complains because H2 sets this index
072                        // automatically
073                        @Index(name = "FK_TRMVALUESET_RES", columnList = "RES_ID")
074                })
075@Entity()
076@IdClass(IdAndPartitionId.class)
077public class TermValueSet extends BasePartitionable implements Serializable {
078        public static final int MAX_EXPANSION_STATUS_LENGTH = 50;
079        public static final int MAX_NAME_LENGTH = 200;
080        public static final int MAX_URL_LENGTH = 200;
081        public static final int MAX_VER_LENGTH = 200;
082        private static final long serialVersionUID = 1L;
083
084        @Id()
085        @SequenceGenerator(name = "SEQ_VALUESET_PID", sequenceName = "SEQ_VALUESET_PID")
086        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_PID")
087        @Column(name = "PID")
088        private Long myId;
089
090        @Column(name = "URL", nullable = false, length = MAX_URL_LENGTH)
091        private String myUrl;
092
093        @Column(name = "VER", nullable = true, length = MAX_VER_LENGTH)
094        private String myVersion;
095
096        @ManyToOne()
097        @JoinColumns(
098                        value = {
099                                @JoinColumn(
100                                                name = "RES_ID",
101                                                referencedColumnName = "RES_ID",
102                                                nullable = false,
103                                                insertable = false,
104                                                updatable = false),
105                                @JoinColumn(
106                                                name = "PARTITION_ID",
107                                                referencedColumnName = "PARTITION_ID",
108                                                nullable = false,
109                                                insertable = false,
110                                                updatable = false)
111                        },
112                        foreignKey = @ForeignKey(name = "FK_TRMVALUESET_RES"))
113        private ResourceTable myResource;
114
115        @Column(name = "RES_ID", nullable = false)
116        private Long myResourcePid;
117
118        @Column(name = "VSNAME", nullable = true, length = MAX_NAME_LENGTH)
119        private String myName;
120
121        @OneToMany(mappedBy = "myValueSet", fetch = FetchType.LAZY)
122        private List<TermValueSetConcept> myConcepts;
123
124        @Column(name = "TOTAL_CONCEPTS", nullable = false)
125        @ColumnDefault("0")
126        private Long myTotalConcepts;
127
128        @Column(name = "TOTAL_CONCEPT_DESIGNATIONS", nullable = false)
129        @ColumnDefault("0")
130        private Long myTotalConceptDesignations;
131
132        @Enumerated(EnumType.STRING)
133        @JdbcTypeCode(SqlTypes.VARCHAR)
134        @Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH)
135        private TermValueSetPreExpansionStatusEnum myExpansionStatus;
136
137        @Temporal(TemporalType.TIMESTAMP)
138        @Column(name = "EXPANDED_AT", nullable = true)
139        private Date myExpansionTimestamp;
140
141        @Transient
142        private transient Integer myHashCode;
143
144        public TermValueSet() {
145                super();
146                myExpansionStatus = TermValueSetPreExpansionStatusEnum.NOT_EXPANDED;
147                myTotalConcepts = 0L;
148                myTotalConceptDesignations = 0L;
149        }
150
151        public Date getExpansionTimestamp() {
152                return myExpansionTimestamp;
153        }
154
155        public void setExpansionTimestamp(Date theExpansionTimestamp) {
156                myExpansionTimestamp = theExpansionTimestamp;
157        }
158
159        public Long getId() {
160                return myId;
161        }
162
163        public IdAndPartitionId getPartitionedId() {
164                return IdAndPartitionId.forId(myId, this);
165        }
166
167        public String getUrl() {
168                return myUrl;
169        }
170
171        public TermValueSet setUrl(@Nonnull String theUrl) {
172                ValidateUtil.isNotBlankOrThrowIllegalArgument(theUrl, "theUrl must not be null or empty");
173                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
174                                theUrl, MAX_URL_LENGTH, "URL exceeds maximum length (" + MAX_URL_LENGTH + "): " + length(theUrl));
175                myUrl = theUrl;
176                return this;
177        }
178
179        public ResourceTable getResource() {
180                return myResource;
181        }
182
183        public TermValueSet setResource(ResourceTable theResource) {
184                myResource = theResource;
185                myResourcePid = theResource.getId().getId();
186                setPartitionId(theResource.getPartitionId());
187                return this;
188        }
189
190        public String getName() {
191                return myName;
192        }
193
194        public TermValueSet setName(String theName) {
195                myName = left(theName, MAX_NAME_LENGTH);
196                return this;
197        }
198
199        public List<TermValueSetConcept> getConcepts() {
200                if (myConcepts == null) {
201                        myConcepts = new ArrayList<>();
202                }
203
204                return myConcepts;
205        }
206
207        public Long getTotalConcepts() {
208                return myTotalConcepts;
209        }
210
211        public TermValueSet setTotalConcepts(Long theTotalConcepts) {
212                myTotalConcepts = theTotalConcepts;
213                return this;
214        }
215
216        public TermValueSet decrementTotalConcepts() {
217                if (myTotalConcepts > 0) {
218                        myTotalConcepts--;
219                }
220                return this;
221        }
222
223        public TermValueSet incrementTotalConcepts() {
224                myTotalConcepts++;
225                return this;
226        }
227
228        public Long getTotalConceptDesignations() {
229                return myTotalConceptDesignations;
230        }
231
232        public TermValueSet setTotalConceptDesignations(Long theTotalConceptDesignations) {
233                myTotalConceptDesignations = theTotalConceptDesignations;
234                return this;
235        }
236
237        public TermValueSet decrementTotalConceptDesignations() {
238                if (myTotalConceptDesignations > 0) {
239                        myTotalConceptDesignations--;
240                }
241                return this;
242        }
243
244        public TermValueSet incrementTotalConceptDesignations() {
245                myTotalConceptDesignations++;
246                return this;
247        }
248
249        public TermValueSetPreExpansionStatusEnum getExpansionStatus() {
250                return myExpansionStatus;
251        }
252
253        public void setExpansionStatus(TermValueSetPreExpansionStatusEnum theExpansionStatus) {
254                myExpansionStatus = theExpansionStatus;
255        }
256
257        public String getVersion() {
258                return myVersion;
259        }
260
261        public TermValueSet setVersion(String theVersion) {
262                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
263                                theVersion,
264                                MAX_VER_LENGTH,
265                                "Version exceeds maximum length (" + MAX_VER_LENGTH + "): " + length(theVersion));
266                myVersion = theVersion;
267                return this;
268        }
269
270        @Override
271        public boolean equals(Object theO) {
272                if (this == theO) return true;
273
274                if (!(theO instanceof TermValueSet)) return false;
275
276                TermValueSet that = (TermValueSet) theO;
277
278                return new EqualsBuilder().append(getUrl(), that.getUrl()).isEquals();
279        }
280
281        @Override
282        public int hashCode() {
283                if (myHashCode == null) {
284                        myHashCode = getUrl().hashCode();
285                }
286                return myHashCode;
287        }
288
289        @Override
290        public String toString() {
291                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
292                                .append("id", myId)
293                                .append("url", myUrl)
294                                .append("version", myVersion)
295                                .append("resourcePid", myResourcePid)
296                                .append("name", myName)
297                                .append(myConcepts != null ? ("conceptCount=" + myConcepts.size()) : ("concepts=(null)"))
298                                .append("totalConcepts", myTotalConcepts)
299                                .append("totalConceptDesignations", myTotalConceptDesignations)
300                                .append("expansionStatus", myExpansionStatus)
301                                .append(myResource != null ? ("resId=" + myResource) : ("resource=(null)"))
302                                .toString();
303        }
304}