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.annotation.Nullable;
028import jakarta.persistence.Column;
029import jakarta.persistence.Entity;
030import jakarta.persistence.FetchType;
031import jakarta.persistence.ForeignKey;
032import jakarta.persistence.GeneratedValue;
033import jakarta.persistence.GenerationType;
034import jakarta.persistence.Id;
035import jakarta.persistence.IdClass;
036import jakarta.persistence.Index;
037import jakarta.persistence.JoinColumn;
038import jakarta.persistence.JoinColumns;
039import jakarta.persistence.ManyToOne;
040import jakarta.persistence.OneToMany;
041import jakarta.persistence.SequenceGenerator;
042import jakarta.persistence.Table;
043import jakarta.persistence.UniqueConstraint;
044import org.apache.commons.lang3.builder.EqualsBuilder;
045import org.apache.commons.lang3.builder.HashCodeBuilder;
046import org.apache.commons.lang3.builder.ToStringBuilder;
047import org.apache.commons.lang3.builder.ToStringStyle;
048
049import java.io.Serializable;
050import java.util.ArrayList;
051import java.util.Collection;
052
053import static org.apache.commons.lang3.StringUtils.length;
054
055@Table(
056                name = "TRM_CODESYSTEM_VER",
057                // Note, we used to have a constraint named IDX_CSV_RESOURCEPID_AND_VER (don't reuse this)
058                uniqueConstraints = {
059                        @UniqueConstraint(
060                                        name = TermCodeSystemVersion.IDX_CODESYSTEM_AND_VER,
061                                        columnNames = {"PARTITION_ID", "CODESYSTEM_PID", "CS_VERSION_ID"})
062                },
063                indexes = {
064                        @Index(name = "FK_CODESYSVER_RES_ID", columnList = "RES_ID"),
065                        @Index(name = "FK_CODESYSVER_CS_ID", columnList = "CODESYSTEM_PID")
066                })
067@Entity()
068@IdClass(IdAndPartitionId.class)
069public class TermCodeSystemVersion extends BasePartitionable implements Serializable {
070        public static final String IDX_CODESYSTEM_AND_VER = "IDX_CODESYSTEM_AND_VER";
071        public static final int MAX_VERSION_LENGTH = 200;
072        private static final long serialVersionUID = 1L;
073
074        @OneToMany(fetch = FetchType.LAZY, mappedBy = "myCodeSystem")
075        private Collection<TermConcept> myConcepts;
076
077        @Id
078        @SequenceGenerator(name = "SEQ_CODESYSTEMVER_PID", sequenceName = "SEQ_CODESYSTEMVER_PID")
079        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODESYSTEMVER_PID")
080        @Column(name = "PID")
081        private Long myId;
082
083        @ManyToOne(fetch = FetchType.LAZY)
084        @JoinColumns(
085                        value = {
086                                @JoinColumn(
087                                                name = "RES_ID",
088                                                referencedColumnName = "RES_ID",
089                                                nullable = false,
090                                                insertable = false,
091                                                updatable = false),
092                                @JoinColumn(
093                                                name = "PARTITION_ID",
094                                                referencedColumnName = "PARTITION_ID",
095                                                nullable = false,
096                                                insertable = false,
097                                                updatable = false)
098                        },
099                        foreignKey = @ForeignKey(name = "FK_CODESYSVER_RES_ID"))
100        private ResourceTable myResource;
101
102        @Column(name = "RES_ID", nullable = false)
103        private Long myResourcePid;
104
105        @Column(name = "CS_VERSION_ID", nullable = true, updatable = true, length = MAX_VERSION_LENGTH)
106        private String myCodeSystemVersionId;
107
108        /**
109         * This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration
110         * issued. It should be made non-nullable at some point.
111         */
112        @ManyToOne(fetch = FetchType.LAZY)
113        @JoinColumns(
114                        value = {
115                                @JoinColumn(
116                                                name = "CODESYSTEM_PID",
117                                                referencedColumnName = "PID",
118                                                insertable = false,
119                                                updatable = false,
120                                                nullable = true),
121                                @JoinColumn(
122                                                name = "PARTITION_ID",
123                                                referencedColumnName = "PARTITION_ID",
124                                                insertable = false,
125                                                nullable = true,
126                                                updatable = false)
127                        },
128                        foreignKey = @ForeignKey(name = "FK_CODESYSVER_CS_ID"))
129        private TermCodeSystem myCodeSystem;
130
131        @Column(name = "CODESYSTEM_PID", insertable = true, updatable = true, nullable = true)
132        private Long myCodeSystemPid;
133
134        @Column(name = "CS_DISPLAY", nullable = true, updatable = true, length = MAX_VERSION_LENGTH)
135        private String myCodeSystemDisplayName;
136
137        /**
138         * Constructor
139         */
140        public TermCodeSystemVersion() {
141                super();
142        }
143
144        public TermCodeSystem getCodeSystem() {
145                return myCodeSystem;
146        }
147
148        public TermCodeSystemVersion setCodeSystem(TermCodeSystem theCodeSystem) {
149                myCodeSystem = theCodeSystem;
150                myCodeSystemPid = theCodeSystem.getPid();
151                assert myCodeSystemPid != null;
152                return this;
153        }
154
155        public String getCodeSystemVersionId() {
156                return myCodeSystemVersionId;
157        }
158
159        public TermCodeSystemVersion setCodeSystemVersionId(String theCodeSystemVersionId) {
160                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
161                                theCodeSystemVersionId,
162                                MAX_VERSION_LENGTH,
163                                "Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemVersionId));
164                myCodeSystemVersionId = theCodeSystemVersionId;
165                return this;
166        }
167
168        public Collection<TermConcept> getConcepts() {
169                if (myConcepts == null) {
170                        myConcepts = new ArrayList<>();
171                }
172                return myConcepts;
173        }
174
175        @Nullable
176        public Long getPid() {
177                return myId;
178        }
179
180        @Nonnull
181        public IdAndPartitionId getId() {
182                return IdAndPartitionId.forId(myId, this);
183        }
184
185        public ResourceTable getResource() {
186                return myResource;
187        }
188
189        public TermCodeSystemVersion setResource(ResourceTable theResource) {
190                myResource = theResource;
191                myResourcePid = theResource.getId().getId();
192                setPartitionId(theResource.getPartitionId());
193                return this;
194        }
195
196        public TermCodeSystemVersion setId(Long theId) {
197                myId = theId;
198                return this;
199        }
200
201        @Override
202        public boolean equals(Object theO) {
203                if (this == theO) {
204                        return true;
205                }
206
207                if (theO == null || getClass() != theO.getClass()) {
208                        return false;
209                }
210
211                TermCodeSystemVersion that = (TermCodeSystemVersion) theO;
212
213                return new EqualsBuilder()
214                                .append(myCodeSystemVersionId, that.myCodeSystemVersionId)
215                                .append(myCodeSystemPid, that.myCodeSystemPid)
216                                .isEquals();
217        }
218
219        @Override
220        public int hashCode() {
221                HashCodeBuilder b = new HashCodeBuilder(17, 37);
222                b.append(myCodeSystemVersionId);
223                b.append(myCodeSystemPid);
224                return b.toHashCode();
225        }
226
227        public String getCodeSystemDisplayName() {
228                return myCodeSystemDisplayName;
229        }
230
231        public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
232                ValidateUtil.isNotTooLongOrThrowIllegalArgument(
233                                theCodeSystemDisplayName,
234                                MAX_VERSION_LENGTH,
235                                "Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemDisplayName));
236                myCodeSystemDisplayName = theCodeSystemDisplayName;
237        }
238
239        public TermConcept addConcept() {
240                TermConcept concept = new TermConcept();
241                concept.setCodeSystemVersion(this);
242                getConcepts().add(concept);
243                return concept;
244        }
245
246        @Override
247        public String toString() {
248                ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
249                b.append("pid", myId);
250                b.append("displayName", myCodeSystemDisplayName);
251                b.append("codeSystemResourcePid", myResourcePid);
252                b.append("codeSystemPid", myCodeSystemPid);
253                b.append("codeSystemVersionId", myCodeSystemVersionId);
254                return b.toString();
255        }
256
257        TermCodeSystemVersion setCodeSystemPidForUnitTest(long theCodeSystemPid) {
258                myCodeSystemPid = theCodeSystemPid;
259                return this;
260        }
261}