001/*
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 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.util.ValidateUtil;
023import com.google.common.annotations.VisibleForTesting;
024import jakarta.annotation.Nonnull;
025import jakarta.persistence.Column;
026import jakarta.persistence.Entity;
027import jakarta.persistence.FetchType;
028import jakarta.persistence.ForeignKey;
029import jakarta.persistence.GeneratedValue;
030import jakarta.persistence.GenerationType;
031import jakarta.persistence.Id;
032import jakarta.persistence.JoinColumn;
033import jakarta.persistence.Lob;
034import jakarta.persistence.ManyToOne;
035import jakarta.persistence.OneToMany;
036import jakarta.persistence.SequenceGenerator;
037import jakarta.persistence.Table;
038import jakarta.persistence.Transient;
039import jakarta.persistence.UniqueConstraint;
040import org.apache.commons.lang3.builder.EqualsBuilder;
041import org.apache.commons.lang3.builder.HashCodeBuilder;
042import org.apache.commons.lang3.builder.ToStringBuilder;
043import org.apache.commons.lang3.builder.ToStringStyle;
044import org.hibernate.Length;
045
046import java.io.Serializable;
047import java.util.ArrayList;
048import java.util.List;
049
050import static java.util.Objects.nonNull;
051import static org.apache.commons.lang3.StringUtils.isNotEmpty;
052import static org.apache.commons.lang3.StringUtils.left;
053import static org.apache.commons.lang3.StringUtils.length;
054
055/*
056 * DM 2019-08-01 - Do not use IDX_VALUESET_CONCEPT_CS_CD or IDX_VALUESET_CONCEPT_CS_CODE; this was previously used as an index so reusing the name will
057 * bork up migration tasks.
058 */
059@Table(
060                name = "TRM_VALUESET_CONCEPT",
061                uniqueConstraints = {
062                        @UniqueConstraint(
063                                        name = "IDX_VS_CONCEPT_CSCD",
064                                        columnNames = {"VALUESET_PID", "SYSTEM_URL", "CODEVAL"}),
065                        @UniqueConstraint(
066                                        name = "IDX_VS_CONCEPT_ORDER",
067                                        columnNames = {"VALUESET_PID", "VALUESET_ORDER"})
068                })
069@Entity()
070public class TermValueSetConcept implements Serializable {
071        private static final long serialVersionUID = 1L;
072
073        @Id()
074        @SequenceGenerator(name = "SEQ_VALUESET_CONCEPT_PID", sequenceName = "SEQ_VALUESET_CONCEPT_PID")
075        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_CONCEPT_PID")
076        @Column(name = "PID")
077        private Long myId;
078
079        @ManyToOne(fetch = FetchType.LAZY)
080        @JoinColumn(
081                        name = "VALUESET_PID",
082                        referencedColumnName = "PID",
083                        nullable = false,
084                        foreignKey = @ForeignKey(name = "FK_TRM_VALUESET_PID"))
085        private TermValueSet myValueSet;
086
087        @Column(name = "VALUESET_PID", insertable = false, updatable = false, nullable = false)
088        private Long myValueSetPid;
089
090        @Column(name = "INDEX_STATUS", nullable = true)
091        private Long myIndexStatus;
092
093        @Column(name = "VALUESET_ORDER", nullable = false)
094        private int myOrder;
095
096        @Transient
097        private String myValueSetUrl;
098
099        @Transient
100        private String myValueSetName;
101
102        @Column(name = "SOURCE_PID", nullable = true)
103        private Long mySourceConceptPid;
104
105        @Deprecated(since = "7.2.0")
106        @Lob
107        @Column(name = "SOURCE_DIRECT_PARENT_PIDS", nullable = true)
108        private String mySourceConceptDirectParentPids;
109
110        @Column(name = "SOURCE_DIRECT_PARENT_PIDS_VC", nullable = true, length = Length.LONG32)
111        private String mySourceConceptDirectParentPidsVc;
112
113        @Column(name = "SYSTEM_URL", nullable = false, length = TermCodeSystem.MAX_URL_LENGTH)
114        private String mySystem;
115
116        @Column(name = "SYSTEM_VER", nullable = true, length = TermCodeSystemVersion.MAX_VERSION_LENGTH)
117        private String mySystemVer;
118
119        @Column(name = "CODEVAL", nullable = false, length = TermConcept.MAX_CODE_LENGTH)
120        private String myCode;
121
122        @Column(name = "DISPLAY", nullable = true, length = TermConcept.MAX_DESC_LENGTH)
123        private String myDisplay;
124
125        @OneToMany(mappedBy = "myConcept", fetch = FetchType.LAZY)
126        private List<TermValueSetConceptDesignation> myDesignations;
127
128        @Transient
129        private transient Integer myHashCode;
130
131        /**
132         * Constructor
133         */
134        public TermValueSetConcept() {
135                super();
136        }
137
138        public Long getId() {
139                return myId;
140        }
141
142        public TermValueSet getValueSet() {
143                return myValueSet;
144        }
145
146        public TermValueSetConcept setValueSet(TermValueSet theValueSet) {
147                myValueSet = theValueSet;
148                return this;
149        }
150
151        public int getOrder() {
152                return myOrder;
153        }
154
155        public TermValueSetConcept setOrder(int theOrder) {
156                myOrder = theOrder;
157                return this;
158        }
159
160        public String getValueSetUrl() {
161                if (myValueSetUrl == null) {
162                        myValueSetUrl = getValueSet().getUrl();
163                }
164
165                return myValueSetUrl;
166        }
167
168        public String getValueSetName() {
169                if (myValueSetName == null) {
170                        myValueSetName = getValueSet().getName();
171                }
172
173                return myValueSetName;
174        }
175
176        public String getSystem() {
177                return mySystem;
178        }
179
180        public TermValueSetConcept setSystem(@Nonnull String theSystem) {
181                ValidateUtil.isNotBlankOrThrowIllegalArgument(theSystem, "theSystem must not be null or empty");
182                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
183                                theSystem,
184                                TermCodeSystem.MAX_URL_LENGTH,
185                                "System exceeds maximum length (" + TermCodeSystem.MAX_URL_LENGTH + "): " + length(theSystem));
186                mySystem = theSystem;
187                return this;
188        }
189
190        public String getSystemVersion() {
191                return mySystemVer;
192        }
193
194        public TermValueSetConcept setSystemVersion(String theSystemVersion) {
195                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
196                                theSystemVersion,
197                                TermCodeSystemVersion.MAX_VERSION_LENGTH,
198                                "System version exceeds maximum length (" + TermCodeSystemVersion.MAX_VERSION_LENGTH + "): "
199                                                + length(theSystemVersion));
200                mySystemVer = theSystemVersion;
201                return this;
202        }
203
204        public String getCode() {
205                return myCode;
206        }
207
208        public TermValueSetConcept setCode(@Nonnull String theCode) {
209                ValidateUtil.isNotBlankOrThrowIllegalArgument(theCode, "theCode must not be null or empty");
210                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
211                                theCode,
212                                TermConcept.MAX_CODE_LENGTH,
213                                "Code exceeds maximum length (" + TermConcept.MAX_CODE_LENGTH + "): " + length(theCode));
214                myCode = theCode;
215                return this;
216        }
217
218        public String getDisplay() {
219                return myDisplay;
220        }
221
222        public TermValueSetConcept setDisplay(String theDisplay) {
223                myDisplay = left(theDisplay, TermConcept.MAX_DESC_LENGTH);
224                return this;
225        }
226
227        public List<TermValueSetConceptDesignation> getDesignations() {
228                if (myDesignations == null) {
229                        myDesignations = new ArrayList<>();
230                }
231
232                return myDesignations;
233        }
234
235        @Override
236        public boolean equals(Object theO) {
237                if (this == theO) return true;
238
239                if (!(theO instanceof TermValueSetConcept)) return false;
240
241                TermValueSetConcept that = (TermValueSetConcept) theO;
242
243                return new EqualsBuilder()
244                                .append(myValueSetPid, that.myValueSetPid)
245                                .append(getSystem(), that.getSystem())
246                                .append(getCode(), that.getCode())
247                                .isEquals();
248        }
249
250        @Override
251        public int hashCode() {
252                if (myHashCode == null) {
253                        myHashCode = new HashCodeBuilder(17, 37)
254                                        .append(myValueSetPid)
255                                        .append(getSystem())
256                                        .append(getCode())
257                                        .toHashCode();
258                }
259                return myHashCode;
260        }
261
262        @Override
263        public String toString() {
264                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
265                                .append("id", myId)
266                                .append("order", myOrder)
267                                .append("system", mySystem)
268                                .append("code", myCode)
269                                .append("valueSet", myValueSet != null ? myValueSet.getId() : "(null)")
270                                .append("valueSetPid", myValueSetPid)
271                                .append("valueSetUrl", this.getValueSetUrl())
272                                .append("valueSetName", this.getValueSetName())
273                                .append("display", myDisplay)
274                                .append("designationCount", myDesignations != null ? myDesignations.size() : "(null)")
275                                .append("parentPids", getSourceConceptDirectParentPids())
276                                .toString();
277        }
278
279        public Long getIndexStatus() {
280                return myIndexStatus;
281        }
282
283        public void setIndexStatus(Long theIndexStatus) {
284                myIndexStatus = theIndexStatus;
285        }
286
287        public void setSourceConceptPid(Long theSourceConceptPid) {
288                mySourceConceptPid = theSourceConceptPid;
289        }
290
291        public void setSourceConceptDirectParentPids(String theSourceConceptDirectParentPids) {
292                mySourceConceptDirectParentPids = theSourceConceptDirectParentPids;
293                mySourceConceptDirectParentPidsVc = theSourceConceptDirectParentPids;
294        }
295
296        public String getSourceConceptDirectParentPids() {
297                return isNotEmpty(mySourceConceptDirectParentPidsVc)
298                                ? mySourceConceptDirectParentPidsVc
299                                : mySourceConceptDirectParentPids;
300        }
301
302        public void clearSourceConceptDirectParentPidsLob() {
303                mySourceConceptDirectParentPids = null;
304        }
305
306        @VisibleForTesting
307        public boolean hasSourceConceptDirectParentPidsLob() {
308                return nonNull(mySourceConceptDirectParentPids);
309        }
310}