001package ca.uhn.fhir.jpa.model.entity;
002
003/*
004 * #%L
005 * HAPI FHIR JPA Model
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import org.apache.commons.lang3.Validate;
024import org.apache.commons.lang3.builder.EqualsBuilder;
025import org.apache.commons.lang3.builder.HashCodeBuilder;
026import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
027import org.hl7.fhir.instance.model.api.IIdType;
028
029import javax.annotation.Nullable;
030import javax.persistence.Column;
031import javax.persistence.Entity;
032import javax.persistence.FetchType;
033import javax.persistence.ForeignKey;
034import javax.persistence.GeneratedValue;
035import javax.persistence.GenerationType;
036import javax.persistence.Id;
037import javax.persistence.Index;
038import javax.persistence.JoinColumn;
039import javax.persistence.ManyToOne;
040import javax.persistence.SequenceGenerator;
041import javax.persistence.Table;
042import javax.persistence.Temporal;
043import javax.persistence.TemporalType;
044import javax.persistence.Transient;
045import java.util.Date;
046
047@Entity
048@Table(name = "HFJ_RES_LINK", indexes = {
049        @Index(name = "IDX_RL_TPATHRES", columnList = "SRC_PATH,TARGET_RESOURCE_ID"),
050        @Index(name = "IDX_RL_SRC", columnList = "SRC_RESOURCE_ID"),
051        @Index(name = "IDX_RL_DEST", columnList = "TARGET_RESOURCE_ID")
052})
053public class ResourceLink extends BaseResourceIndex {
054
055        public static final int SRC_PATH_LENGTH = 500;
056        private static final long serialVersionUID = 1L;
057        @SequenceGenerator(name = "SEQ_RESLINK_ID", sequenceName = "SEQ_RESLINK_ID")
058        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESLINK_ID")
059        @Id
060        @Column(name = "PID")
061        private Long myId;
062
063        @Column(name = "SRC_PATH", length = SRC_PATH_LENGTH, nullable = false)
064        private String mySourcePath;
065
066        @ManyToOne(optional = false, fetch = FetchType.LAZY)
067        @JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESLINK_SOURCE"))
068        private ResourceTable mySourceResource;
069
070        @Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
071        private Long mySourceResourcePid;
072
073        @Column(name = "SOURCE_RESOURCE_TYPE", updatable = false, nullable = false, length = ResourceTable.RESTYPE_LEN)
074        @FullTextField
075        private String mySourceResourceType;
076
077        @ManyToOne(optional = true, fetch = FetchType.LAZY)
078        @JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = true, insertable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_RESLINK_TARGET"))
079        private ResourceTable myTargetResource;
080
081        @Column(name = "TARGET_RESOURCE_ID", insertable = true, updatable = true, nullable = true)
082        @FullTextField
083        private Long myTargetResourcePid;
084
085        @Column(name = "TARGET_RESOURCE_TYPE", nullable = false, length = ResourceTable.RESTYPE_LEN)
086        @FullTextField
087        private String myTargetResourceType;
088
089        @Column(name = "TARGET_RESOURCE_URL", length = 200, nullable = true)
090        @FullTextField
091        private String myTargetResourceUrl;
092        @Column(name = "TARGET_RESOURCE_VERSION", nullable = true)
093        private Long myTargetResourceVersion;
094        @FullTextField
095        @Column(name = "SP_UPDATED", nullable = true) // TODO: make this false after HAPI 2.3
096        @Temporal(TemporalType.TIMESTAMP)
097        private Date myUpdated;
098        @Transient
099        private transient String myTargetResourceId;
100
101        public ResourceLink() {
102                super();
103        }
104
105        public Long getTargetResourceVersion() {
106                return myTargetResourceVersion;
107        }
108
109        public void setTargetResourceVersion(Long theTargetResourceVersion) {
110                myTargetResourceVersion = theTargetResourceVersion;
111        }
112
113        public String getTargetResourceId() {
114                if (myTargetResourceId == null && myTargetResource != null) {
115                        myTargetResourceId = getTargetResource().getIdDt().getIdPart();
116                }
117                return myTargetResourceId;
118        }
119
120        public String getTargetResourceType() {
121                return myTargetResourceType;
122        }
123
124        @Override
125        public boolean equals(Object theObj) {
126                if (this == theObj) {
127                        return true;
128                }
129                if (theObj == null) {
130                        return false;
131                }
132                if (!(theObj instanceof ResourceLink)) {
133                        return false;
134                }
135                ResourceLink obj = (ResourceLink) theObj;
136                EqualsBuilder b = new EqualsBuilder();
137                b.append(mySourcePath, obj.mySourcePath);
138                b.append(mySourceResource, obj.mySourceResource);
139                b.append(myTargetResourceUrl, obj.myTargetResourceUrl);
140                b.append(myTargetResourceType, obj.myTargetResourceType);
141                b.append(myTargetResourceVersion, obj.myTargetResourceVersion);
142                b.append(getTargetResourceId(), obj.getTargetResourceId());
143                return b.isEquals();
144        }
145
146        @Override
147        public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) {
148                ResourceLink source = (ResourceLink) theSource;
149                mySourcePath = source.getSourcePath();
150                myTargetResource = source.getTargetResource();
151                myTargetResourceId = source.getTargetResourceId();
152                myTargetResourcePid = source.getTargetResourcePid();
153                myTargetResourceType = source.getTargetResourceType();
154                myTargetResourceVersion = source.getTargetResourceVersion();
155                myTargetResourceUrl = source.getTargetResourceUrl();
156        }
157
158        public String getSourcePath() {
159                return mySourcePath;
160        }
161
162        public void setSourcePath(String theSourcePath) {
163                mySourcePath = theSourcePath;
164        }
165
166        public Long getSourceResourcePid() {
167                return mySourceResourcePid;
168        }
169
170        public ResourceTable getSourceResource() {
171                return mySourceResource;
172        }
173
174        public void setSourceResource(ResourceTable theSourceResource) {
175                mySourceResource = theSourceResource;
176                mySourceResourcePid = theSourceResource.getId();
177                mySourceResourceType = theSourceResource.getResourceType();
178        }
179
180        public void setTargetResource(String theResourceType, Long theResourcePid, String theTargetResourceId) {
181                Validate.notBlank(theResourceType);
182
183                myTargetResourceType = theResourceType;
184                myTargetResourcePid = theResourcePid;
185                myTargetResourceId = theTargetResourceId;
186        }
187
188        public String getTargetResourceUrl() {
189                return myTargetResourceUrl;
190        }
191
192        public void setTargetResourceUrl(IIdType theTargetResourceUrl) {
193                Validate.isTrue(theTargetResourceUrl.hasBaseUrl());
194                Validate.isTrue(theTargetResourceUrl.hasResourceType());
195
196//              if (theTargetResourceUrl.hasIdPart()) {
197                // do nothing
198//              } else {
199                // Must have set an url like http://example.org/something
200                // We treat 'something' as the resource type because of fix for #659. Prior to #659 fix, 'something' was
201                // treated as the id and 'example.org' was treated as the resource type
202                // TODO: log a warning?
203//              }
204
205                myTargetResourceType = theTargetResourceUrl.getResourceType();
206                myTargetResourceUrl = theTargetResourceUrl.getValue();
207        }
208
209        public Long getTargetResourcePid() {
210                return myTargetResourcePid;
211        }
212
213        public void setTargetResourceUrlCanonical(String theTargetResourceUrl) {
214                Validate.notBlank(theTargetResourceUrl);
215
216                myTargetResourceType = "(unknown)";
217                myTargetResourceUrl = theTargetResourceUrl;
218        }
219
220        public Date getUpdated() {
221                return myUpdated;
222        }
223
224        public void setUpdated(Date theUpdated) {
225                myUpdated = theUpdated;
226        }
227
228        @Override
229        public Long getId() {
230                return myId;
231        }
232
233        @Override
234        public void setId(Long theId) {
235                myId = theId;
236        }
237
238        @Override
239        public void clearHashes() {
240                // nothing right now
241        }
242
243        @Override
244        public void calculateHashes() {
245                // nothing right now
246        }
247
248        @Override
249        public int hashCode() {
250                HashCodeBuilder b = new HashCodeBuilder();
251                b.append(mySourcePath);
252                b.append(mySourceResource);
253                b.append(myTargetResourceUrl);
254                b.append(myTargetResourceVersion);
255                b.append(getTargetResourceType());
256                b.append(getTargetResourceId());
257                return b.toHashCode();
258        }
259
260        @Override
261        public String toString() {
262                StringBuilder b = new StringBuilder();
263                b.append("ResourceLink[");
264                b.append("path=").append(mySourcePath);
265                b.append(", src=").append(mySourceResourcePid);
266                b.append(", target=").append(myTargetResourcePid);
267                b.append(", targetType=").append(myTargetResourceType);
268                b.append(", targetVersion=").append(myTargetResourceVersion);
269                b.append(", targetUrl=").append(myTargetResourceUrl);
270
271                b.append("]");
272                return b.toString();
273        }
274
275        public ResourceTable getTargetResource() {
276                return myTargetResource;
277        }
278
279        public static ResourceLink forAbsoluteReference(String theSourcePath, ResourceTable theSourceResource, IIdType theTargetResourceUrl, Date theUpdated) {
280                ResourceLink retVal = new ResourceLink();
281                retVal.setSourcePath(theSourcePath);
282                retVal.setSourceResource(theSourceResource);
283                retVal.setTargetResourceUrl(theTargetResourceUrl);
284                retVal.setUpdated(theUpdated);
285                return retVal;
286        }
287
288        /**
289         * Factory for canonical URL
290         */
291        public static ResourceLink forLogicalReference(String theSourcePath, ResourceTable theSourceResource, String theTargetResourceUrl, Date theUpdated) {
292                ResourceLink retVal = new ResourceLink();
293                retVal.setSourcePath(theSourcePath);
294                retVal.setSourceResource(theSourceResource);
295                retVal.setTargetResourceUrlCanonical(theTargetResourceUrl);
296                retVal.setUpdated(theUpdated);
297                return retVal;
298        }
299
300        /**
301         * @param theTargetResourceVersion This should only be populated if the reference actually had a version
302         */
303        public static ResourceLink forLocalReference(String theSourcePath, ResourceTable theSourceResource, String theTargetResourceType, Long theTargetResourcePid, String theTargetResourceId, Date theUpdated, @Nullable Long theTargetResourceVersion) {
304                ResourceLink retVal = new ResourceLink();
305                retVal.setSourcePath(theSourcePath);
306                retVal.setSourceResource(theSourceResource);
307                retVal.setTargetResource(theTargetResourceType, theTargetResourcePid, theTargetResourceId);
308                retVal.setTargetResourceVersion(theTargetResourceVersion);
309                retVal.setUpdated(theUpdated);
310                return retVal;
311        }
312
313}