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