
001/* 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2023 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 org.apache.commons.lang3.Validate; 024import org.apache.commons.lang3.builder.EqualsBuilder; 025import org.apache.commons.lang3.builder.HashCodeBuilder; 026import org.apache.commons.lang3.builder.ToStringBuilder; 027import org.apache.commons.lang3.builder.ToStringStyle; 028import org.hibernate.search.engine.backend.types.Projectable; 029import org.hibernate.search.engine.backend.types.Searchable; 030import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; 031import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; 032import org.hibernate.validator.constraints.NotBlank; 033 034import java.io.Serializable; 035import java.nio.charset.StandardCharsets; 036import javax.annotation.Nonnull; 037import javax.persistence.Column; 038import javax.persistence.Entity; 039import javax.persistence.FetchType; 040import javax.persistence.ForeignKey; 041import javax.persistence.GeneratedValue; 042import javax.persistence.GenerationType; 043import javax.persistence.Id; 044import javax.persistence.Index; 045import javax.persistence.JoinColumn; 046import javax.persistence.Lob; 047import javax.persistence.ManyToOne; 048import javax.persistence.SequenceGenerator; 049import javax.persistence.Table; 050 051import static org.apache.commons.lang3.StringUtils.left; 052import static org.apache.commons.lang3.StringUtils.length; 053 054@Entity 055@Table( 056 name = "TRM_CONCEPT_PROPERTY", 057 uniqueConstraints = {}, 058 indexes = { 059 // must have same name that indexed FK or SchemaMigrationTest complains because H2 sets this index 060 // automatically 061 @Index(name = "FK_CONCEPTPROP_CONCEPT", columnList = "CONCEPT_PID", unique = false), 062 @Index(name = "FK_CONCEPTPROP_CSV", columnList = "CS_VER_PID") 063 }) 064public class TermConceptProperty implements Serializable { 065 public static final int MAX_PROPTYPE_ENUM_LENGTH = 6; 066 private static final long serialVersionUID = 1L; 067 private static final int MAX_LENGTH = 500; 068 069 @ManyToOne(fetch = FetchType.LAZY) 070 @JoinColumn( 071 name = "CONCEPT_PID", 072 referencedColumnName = "PID", 073 foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT")) 074 private TermConcept myConcept; 075 076 /** 077 * TODO: Make this non-null 078 * 079 * @since 3.5.0 080 */ 081 @ManyToOne(fetch = FetchType.LAZY) 082 @JoinColumn( 083 name = "CS_VER_PID", 084 nullable = true, 085 referencedColumnName = "PID", 086 foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CSV")) 087 private TermCodeSystemVersion myCodeSystemVersion; 088 089 @Id() 090 @SequenceGenerator(name = "SEQ_CONCEPT_PROP_PID", sequenceName = "SEQ_CONCEPT_PROP_PID") 091 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PROP_PID") 092 @Column(name = "PID") 093 private Long myId; 094 095 @Column(name = "PROP_KEY", nullable = false, length = MAX_LENGTH) 096 @NotBlank 097 @GenericField(searchable = Searchable.YES) 098 private String myKey; 099 100 @Column(name = "PROP_VAL", nullable = true, length = MAX_LENGTH) 101 @FullTextField(searchable = Searchable.YES, projectable = Projectable.YES, analyzer = "standardAnalyzer") 102 @GenericField(name = "myValueString", searchable = Searchable.YES) 103 private String myValue; 104 105 @Column(name = "PROP_VAL_LOB") 106 @Lob() 107 private byte[] myValueLob; 108 109 @Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH) 110 private TermConceptPropertyTypeEnum myType; 111 112 /** 113 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 114 */ 115 @Column(name = "PROP_CODESYSTEM", length = MAX_LENGTH, nullable = true) 116 private String myCodeSystem; 117 118 /** 119 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 120 */ 121 @Column(name = "PROP_DISPLAY", length = MAX_LENGTH, nullable = true) 122 @GenericField(name = "myDisplayString", searchable = Searchable.YES) 123 private String myDisplay; 124 125 /** 126 * Constructor 127 */ 128 public TermConceptProperty() { 129 super(); 130 } 131 132 /** 133 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 134 */ 135 public String getCodeSystem() { 136 return myCodeSystem; 137 } 138 139 /** 140 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 141 */ 142 public TermConceptProperty setCodeSystem(String theCodeSystem) { 143 ValidateUtil.isNotTooLongOrThrowIllegalArgument( 144 theCodeSystem, 145 MAX_LENGTH, 146 "Property code system exceeds maximum length (" + MAX_LENGTH + "): " + length(theCodeSystem)); 147 myCodeSystem = theCodeSystem; 148 return this; 149 } 150 151 /** 152 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 153 */ 154 public String getDisplay() { 155 return myDisplay; 156 } 157 158 /** 159 * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} 160 */ 161 public TermConceptProperty setDisplay(String theDisplay) { 162 myDisplay = left(theDisplay, MAX_LENGTH); 163 return this; 164 } 165 166 public String getKey() { 167 return myKey; 168 } 169 170 public TermConceptProperty setKey(@Nonnull String theKey) { 171 ValidateUtil.isNotBlankOrThrowIllegalArgument(theKey, "theKey must not be null or empty"); 172 ValidateUtil.isNotTooLongOrThrowIllegalArgument( 173 theKey, MAX_LENGTH, "Code exceeds maximum length (" + MAX_LENGTH + "): " + length(theKey)); 174 myKey = theKey; 175 return this; 176 } 177 178 public TermConceptPropertyTypeEnum getType() { 179 return myType; 180 } 181 182 public TermConceptProperty setType(@Nonnull TermConceptPropertyTypeEnum theType) { 183 Validate.notNull(theType); 184 myType = theType; 185 return this; 186 } 187 188 /** 189 * This will contain the value for a {@link TermConceptPropertyTypeEnum#STRING string} 190 * property, and the code for a {@link TermConceptPropertyTypeEnum#CODING coding} property. 191 */ 192 public String getValue() { 193 if (hasValueLob()) { 194 return getValueLobAsString(); 195 } 196 return myValue; 197 } 198 199 /** 200 * This will contain the value for a {@link TermConceptPropertyTypeEnum#STRING string} 201 * property, and the code for a {@link TermConceptPropertyTypeEnum#CODING coding} property. 202 */ 203 public TermConceptProperty setValue(String theValue) { 204 if (theValue.length() > MAX_LENGTH) { 205 setValueLob(theValue); 206 } else { 207 myValueLob = null; 208 } 209 myValue = left(theValue, MAX_LENGTH); 210 return this; 211 } 212 213 public boolean hasValueLob() { 214 if (myValueLob != null && myValueLob.length > 0) { 215 return true; 216 } 217 return false; 218 } 219 220 public byte[] getValueLob() { 221 return myValueLob; 222 } 223 224 public TermConceptProperty setValueLob(byte[] theValueLob) { 225 myValueLob = theValueLob; 226 return this; 227 } 228 229 public TermConceptProperty setValueLob(String theValueLob) { 230 myValueLob = theValueLob.getBytes(StandardCharsets.UTF_8); 231 return this; 232 } 233 234 public String getValueLobAsString() { 235 return new String(myValueLob, StandardCharsets.UTF_8); 236 } 237 238 public TermConceptProperty setCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { 239 myCodeSystemVersion = theCodeSystemVersion; 240 return this; 241 } 242 243 public TermConceptProperty setConcept(TermConcept theConcept) { 244 myConcept = theConcept; 245 return this; 246 } 247 248 @Override 249 public String toString() { 250 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 251 .append("conceptPid", myConcept.getId()) 252 .append("key", myKey) 253 .append("value", getValue()) 254 .toString(); 255 } 256 257 @Override 258 public boolean equals(Object theO) { 259 if (this == theO) { 260 return true; 261 } 262 263 if (theO == null || getClass() != theO.getClass()) { 264 return false; 265 } 266 267 TermConceptProperty that = (TermConceptProperty) theO; 268 269 return new EqualsBuilder() 270 .append(myKey, that.myKey) 271 .append(myValue, that.myValue) 272 .append(myType, that.myType) 273 .append(myCodeSystem, that.myCodeSystem) 274 .append(myDisplay, that.myDisplay) 275 .isEquals(); 276 } 277 278 @Override 279 public int hashCode() { 280 return new HashCodeBuilder(17, 37) 281 .append(myKey) 282 .append(myValue) 283 .append(myType) 284 .append(myCodeSystem) 285 .append(myDisplay) 286 .toHashCode(); 287 } 288 289 public Long getPid() { 290 return myId; 291 } 292}