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}