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 ca.uhn.fhir.context.FhirContext;
024import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
025import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
026import ca.uhn.fhir.jpa.model.search.ExtendedHSearchIndexData;
027import ca.uhn.fhir.jpa.model.search.ResourceTableRoutingBinder;
028import ca.uhn.fhir.jpa.model.search.SearchParamTextPropertyBinder;
029import ca.uhn.fhir.model.primitive.IdDt;
030import ca.uhn.fhir.rest.api.Constants;
031import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
032import org.apache.commons.lang3.builder.ToStringBuilder;
033import org.apache.commons.lang3.builder.ToStringStyle;
034import org.hibernate.Session;
035import org.hibernate.annotations.GenerationTime;
036import org.hibernate.annotations.GeneratorType;
037import org.hibernate.annotations.OptimisticLock;
038import org.hibernate.search.engine.backend.types.Projectable;
039import org.hibernate.search.engine.backend.types.Searchable;
040import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
041import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.RoutingBinderRef;
042import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
043import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
044import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
045import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency;
046import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath;
047import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyBinding;
048import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue;
049import org.hibernate.tuple.ValueGenerator;
050import org.hl7.fhir.instance.model.api.IIdType;
051
052import javax.persistence.CascadeType;
053import javax.persistence.Column;
054import javax.persistence.Entity;
055import javax.persistence.FetchType;
056import javax.persistence.GeneratedValue;
057import javax.persistence.GenerationType;
058import javax.persistence.Id;
059import javax.persistence.Index;
060import javax.persistence.NamedEntityGraph;
061import javax.persistence.OneToMany;
062import javax.persistence.OneToOne;
063import javax.persistence.PrePersist;
064import javax.persistence.PreUpdate;
065import javax.persistence.SequenceGenerator;
066import javax.persistence.Table;
067import javax.persistence.Transient;
068import javax.persistence.Version;
069import java.io.Serializable;
070import java.util.ArrayList;
071import java.util.Collection;
072import java.util.HashSet;
073import java.util.Objects;
074import java.util.Set;
075import java.util.stream.Collectors;
076
077@Indexed(routingBinder= @RoutingBinderRef(type = ResourceTableRoutingBinder.class))
078@Entity
079@Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes = {
080        // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE
081        @Index(name = "IDX_RES_DATE", columnList = "RES_UPDATED"),
082        @Index(name = "IDX_RES_TYPE_DEL_UPDATED", columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"),
083})
084@NamedEntityGraph(name = "Resource.noJoins")
085public class ResourceTable extends BaseHasResource implements Serializable, IBasePersistedResource, IResourceLookup {
086        public static final int RESTYPE_LEN = 40;
087        private static final int MAX_LANGUAGE_LENGTH = 20;
088        private static final long serialVersionUID = 1L;
089
090        /**
091         * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB
092         * Note the extra config needed in HS6 for indexing transient props:
093         * https://docs.jboss.org/hibernate/search/6.0/migration/html_single/#indexed-transient-requires-configuration
094         *
095         * Note that we depend on `myVersion` updated for this field to be indexed.
096         */
097        @Transient
098        @FullTextField(name = "myContentText", searchable = Searchable.YES, projectable = Projectable.YES, analyzer = "standardAnalyzer")
099        @FullTextField(name = "myContentTextEdgeNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompleteEdgeAnalyzer")
100        @FullTextField(name = "myContentTextNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompleteNGramAnalyzer")
101        @FullTextField(name = "myContentTextPhonetic", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompletePhoneticAnalyzer")
102        @OptimisticLock(excluded = true)
103        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
104        private String myContentText;
105
106        @Column(name = "HASH_SHA256", length = 64, nullable = true)
107        @OptimisticLock(excluded = true)
108        private String myHashSha256;
109
110        @Column(name = "SP_HAS_LINKS")
111        @OptimisticLock(excluded = true)
112        private boolean myHasLinks;
113
114        @Id
115        @SequenceGenerator(name = "SEQ_RESOURCE_ID", sequenceName = "SEQ_RESOURCE_ID")
116        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID")
117        @Column(name = "RES_ID")
118        @GenericField(projectable = Projectable.YES)
119        private Long myId;
120
121        @Column(name = "SP_INDEX_STATUS", nullable = true)
122        @OptimisticLock(excluded = true)
123        private Long myIndexStatus;
124
125        // TODO: Removed in 5.5.0. Drop in a future release.
126        @Column(name = "RES_LANGUAGE", length = MAX_LANGUAGE_LENGTH, nullable = true)
127        @OptimisticLock(excluded = true)
128        private String myLanguage;
129
130        /**
131         * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB
132         */
133        @Transient()
134        @FullTextField(name = "myNarrativeText", searchable = Searchable.YES, projectable = Projectable.YES, analyzer = "standardAnalyzer")
135        @FullTextField(name = "myNarrativeTextEdgeNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompleteEdgeAnalyzer")
136        @FullTextField(name = "myNarrativeTextNGram", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompleteNGramAnalyzer")
137        @FullTextField(name = "myNarrativeTextPhonetic", searchable= Searchable.YES, projectable= Projectable.NO, analyzer =  "autocompletePhoneticAnalyzer")
138        @OptimisticLock(excluded = true)
139        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
140        private String myNarrativeText;
141
142        @Transient
143        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
144        @PropertyBinding(binder = @PropertyBinderRef(type = SearchParamTextPropertyBinder.class))
145        private ExtendedHSearchIndexData myLuceneIndexData;
146
147        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
148        @OptimisticLock(excluded = true)
149        private Collection<ResourceIndexedSearchParamCoords> myParamsCoords;
150
151        @Column(name = "SP_COORDS_PRESENT")
152        @OptimisticLock(excluded = true)
153        private boolean myParamsCoordsPopulated;
154
155        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
156        @OptimisticLock(excluded = true)
157        private Collection<ResourceIndexedSearchParamDate> myParamsDate;
158
159        @Column(name = "SP_DATE_PRESENT")
160        @OptimisticLock(excluded = true)
161        private boolean myParamsDatePopulated;
162
163        @OptimisticLock(excluded = true)
164        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
165        private Collection<ResourceIndexedSearchParamNumber> myParamsNumber;
166
167        @Column(name = "SP_NUMBER_PRESENT")
168        @OptimisticLock(excluded = true)
169        private boolean myParamsNumberPopulated;
170
171        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
172        @OptimisticLock(excluded = true)
173        private Collection<ResourceIndexedSearchParamQuantity> myParamsQuantity;
174
175        @Column(name = "SP_QUANTITY_PRESENT")
176        @OptimisticLock(excluded = true)
177        private boolean myParamsQuantityPopulated;
178        
179        /**
180         * Added to support UCUM conversion
181         * since 5.3.0
182         */
183        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
184        @OptimisticLock(excluded = true)
185        private Collection<ResourceIndexedSearchParamQuantityNormalized> myParamsQuantityNormalized;
186        
187        /**
188         * Added to support UCUM conversion, 
189         * NOTE : use Boolean class instead of boolean primitive, in order to set the existing rows to null
190         * since 5.3.0
191         */
192        @Column(name = "SP_QUANTITY_NRML_PRESENT")
193        @OptimisticLock(excluded = true)
194        private Boolean myParamsQuantityNormalizedPopulated = Boolean.FALSE;
195
196        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
197        @OptimisticLock(excluded = true)
198        private Collection<ResourceIndexedSearchParamString> myParamsString;
199
200        @Column(name = "SP_STRING_PRESENT")
201        @OptimisticLock(excluded = true)
202        private boolean myParamsStringPopulated;
203
204        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
205        @OptimisticLock(excluded = true)
206        private Collection<ResourceIndexedSearchParamToken> myParamsToken;
207
208        @Column(name = "SP_TOKEN_PRESENT")
209        @OptimisticLock(excluded = true)
210        private boolean myParamsTokenPopulated;
211
212        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
213        @OptimisticLock(excluded = true)
214        private Collection<ResourceIndexedSearchParamUri> myParamsUri;
215
216        @Column(name = "SP_URI_PRESENT")
217        @OptimisticLock(excluded = true)
218        private boolean myParamsUriPopulated;
219
220        // Added in 3.0.0 - Should make this a primitive Boolean at some point
221        @OptimisticLock(excluded = true)
222        @Column(name = "SP_CMPSTR_UNIQ_PRESENT")
223        private Boolean myParamsComboStringUniquePresent = false;
224
225        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
226        @OptimisticLock(excluded = true)
227        private Collection<ResourceIndexedComboStringUnique> myParamsComboStringUnique;
228
229        // Added in 5.5.0 - Should make this a primitive Boolean at some point
230        @OptimisticLock(excluded = true)
231        @Column(name = "SP_CMPTOKS_PRESENT")
232        private Boolean myParamsComboTokensNonUniquePresent = false;
233
234        @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
235        @OptimisticLock(excluded = true)
236        private Collection<ResourceIndexedComboTokenNonUnique> myParamsComboTokensNonUnique;
237
238        @OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
239        @OptimisticLock(excluded = true)
240        private Collection<ResourceLink> myResourceLinks;
241
242        /**
243         * This is a clone of {@link #myResourceLinks} but without the hibernate annotations.
244         * Before we persist we copy the contents of {@link #myResourceLinks} into this field. We
245         * have this separate because that way we can only populate this field if
246         * {@link #myHasLinks} is true, meaning that there are actually resource links present
247         * right now. This avoids Hibernate Search triggering a select on the resource link
248         * table.
249         * <p>
250         * This field is used by FulltextSearchSvcImpl
251         * <p>
252         * You can test that any changes don't cause extra queries by running
253         * FhirResourceDaoR4QueryCountTest
254         */
255        @FullTextField
256        @Transient
257        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myResourceLinks")))
258        private String myResourceLinksField;
259
260        @OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
261        @OptimisticLock(excluded = true)
262        private Collection<ResourceLink> myResourceLinksAsTarget;
263
264        @Column(name = "RES_TYPE", length = RESTYPE_LEN, nullable = false)
265        @FullTextField
266        @OptimisticLock(excluded = true)
267        private String myResourceType;
268
269        @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
270        @OptimisticLock(excluded = true)
271        private Collection<SearchParamPresentEntity> mySearchParamPresents;
272
273        @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
274        @OptimisticLock(excluded = true)
275        private Set<ResourceTag> myTags;
276
277        @Transient
278        private transient boolean myUnchangedInCurrentOperation;
279
280
281        /**
282         * The id of the Resource.
283         * Will contain either the client-assigned id, or the sequence value.
284         * Will be null during insert time until the first read.
285         *
286         */
287        @Column(name= "FHIR_ID",
288                // [A-Za-z0-9\-\.]{1,64} - https://www.hl7.org/fhir/datatypes.html#id
289                length = 64,
290                // we never update this after insert, and the Generator will otherwise "dirty" the object.
291                updatable = false)
292        // inject the pk for server-assigned sequence ids.
293        @GeneratorType(when = GenerationTime.INSERT, type = FhirIdGenerator.class)
294        // Make sure the generator doesn't bump the history version.
295        @OptimisticLock(excluded = true)
296        private String myFhirId;
297
298        /**
299         * Populate myFhirId with server-assigned sequence id when no client-id provided.
300         * We eat this complexity during insert to simplify query time with a uniform column.
301         * Server-assigned sequence ids aren't available until just before insertion.
302         * Hibernate calls insert Generators after the pk has been assigned, so we can use myId safely here.
303         */
304        public static final class FhirIdGenerator implements ValueGenerator<String> {
305                @Override
306                public String generateValue(Session session, Object owner) {
307                        ResourceTable that = (ResourceTable) owner;
308                        return that.myFhirId != null ? that.myFhirId : that.myId.toString();
309                }
310        }
311
312        @Version
313        @Column(name = "RES_VER")
314        private long myVersion;
315
316        @OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY)
317        private Collection<ResourceHistoryProvenanceEntity> myProvenance;
318
319        @Transient
320        private transient ResourceHistoryTable myCurrentVersionEntity;
321
322        @OneToOne(optional = true, fetch = FetchType.EAGER, cascade = {}, orphanRemoval = false, mappedBy = "myResource")
323        @OptimisticLock(excluded = true)
324        private ForcedId myForcedId;
325
326        @Transient
327        private volatile String myCreatedByMatchUrl;
328
329        /**
330         * Constructor
331         */
332        public ResourceTable() {
333                super();
334        }
335
336        @Override
337        public ResourceTag addTag(TagDefinition theTag) {
338                for (ResourceTag next : getTags()) {
339                        if (next.getTag().equals(theTag)) {
340                                return next;
341                        }
342                }
343                ResourceTag tag = new ResourceTag(this, theTag, getPartitionId());
344                getTags().add(tag);
345                return tag;
346        }
347
348
349        public String getHashSha256() {
350                return myHashSha256;
351        }
352
353        public void setHashSha256(String theHashSha256) {
354                myHashSha256 = theHashSha256;
355        }
356
357        @Override
358        public Long getId() {
359                return myId;
360        }
361
362        public void setId(Long theId) {
363                myId = theId;
364        }
365
366        public Long getIndexStatus() {
367                return myIndexStatus;
368        }
369
370        public void setIndexStatus(Long theIndexStatus) {
371                myIndexStatus = theIndexStatus;
372        }
373
374        public Collection<ResourceIndexedComboStringUnique> getParamsComboStringUnique() {
375                if (myParamsComboStringUnique == null) {
376                        myParamsComboStringUnique = new ArrayList<>();
377                }
378                return myParamsComboStringUnique;
379        }
380
381        public Collection<ResourceIndexedComboTokenNonUnique> getmyParamsComboTokensNonUnique() {
382                if (myParamsComboTokensNonUnique == null) {
383                        myParamsComboTokensNonUnique = new ArrayList<>();
384                }
385                return myParamsComboTokensNonUnique;
386        }
387
388        public Collection<ResourceIndexedSearchParamCoords> getParamsCoords() {
389                if (myParamsCoords == null) {
390                        myParamsCoords = new ArrayList<>();
391                }
392                return myParamsCoords;
393        }
394
395        public void setParamsCoords(Collection<ResourceIndexedSearchParamCoords> theParamsCoords) {
396                if (!isParamsTokenPopulated() && theParamsCoords.isEmpty()) {
397                        return;
398                }
399                getParamsCoords().clear();
400                getParamsCoords().addAll(theParamsCoords);
401        }
402
403        public Collection<ResourceIndexedSearchParamDate> getParamsDate() {
404                if (myParamsDate == null) {
405                        myParamsDate = new ArrayList<>();
406                }
407                return myParamsDate;
408        }
409
410        public void setParamsDate(Collection<ResourceIndexedSearchParamDate> theParamsDate) {
411                if (!isParamsDatePopulated() && theParamsDate.isEmpty()) {
412                        return;
413                }
414                getParamsDate().clear();
415                getParamsDate().addAll(theParamsDate);
416        }
417
418        public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() {
419                if (myParamsNumber == null) {
420                        myParamsNumber = new ArrayList<>();
421                }
422                return myParamsNumber;
423        }
424
425        public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) {
426                if (!isParamsNumberPopulated() && theNumberParams.isEmpty()) {
427                        return;
428                }
429                getParamsNumber().clear();
430                getParamsNumber().addAll(theNumberParams);
431        }
432
433        public Collection<ResourceIndexedSearchParamQuantity> getParamsQuantity() {
434                if (myParamsQuantity == null) {
435                        myParamsQuantity = new ArrayList<>();
436                }
437                return myParamsQuantity;
438        }
439
440        public void setParamsQuantity(Collection<ResourceIndexedSearchParamQuantity> theQuantityParams) {
441                if (!isParamsQuantityPopulated() && theQuantityParams.isEmpty()) {
442                        return;
443                }
444                getParamsQuantity().clear();
445                getParamsQuantity().addAll(theQuantityParams);
446        }
447
448        public Collection<ResourceIndexedSearchParamQuantityNormalized> getParamsQuantityNormalized() {
449                if (myParamsQuantityNormalized == null) {
450                        myParamsQuantityNormalized = new ArrayList<>();
451                }
452                return myParamsQuantityNormalized;
453        }
454
455        public void setParamsQuantityNormalized(Collection<ResourceIndexedSearchParamQuantityNormalized> theQuantityNormalizedParams) {
456                if (!isParamsQuantityNormalizedPopulated() && theQuantityNormalizedParams.isEmpty()) {
457                        return;
458                }
459                getParamsQuantityNormalized().clear();
460                getParamsQuantityNormalized().addAll(theQuantityNormalizedParams);
461        }
462
463        public Collection<ResourceIndexedSearchParamString> getParamsString() {
464                if (myParamsString == null) {
465                        myParamsString = new ArrayList<>();
466                }
467                return myParamsString;
468        }
469
470        public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) {
471                if (!isParamsStringPopulated() && theParamsString.isEmpty()) {
472                        return;
473                }
474                getParamsString().clear();
475                getParamsString().addAll(theParamsString);
476        }
477
478        public Collection<ResourceIndexedSearchParamToken> getParamsToken() {
479                if (myParamsToken == null) {
480                        myParamsToken = new ArrayList<>();
481                }
482                return myParamsToken;
483        }
484
485        public void setParamsToken(Collection<ResourceIndexedSearchParamToken> theParamsToken) {
486                if (!isParamsTokenPopulated() && theParamsToken.isEmpty()) {
487                        return;
488                }
489                getParamsToken().clear();
490                getParamsToken().addAll(theParamsToken);
491        }
492
493        public Collection<ResourceIndexedSearchParamUri> getParamsUri() {
494                if (myParamsUri == null) {
495                        myParamsUri = new ArrayList<>();
496                }
497                return myParamsUri;
498        }
499
500        public void setParamsUri(Collection<ResourceIndexedSearchParamUri> theParamsUri) {
501                if (!isParamsTokenPopulated() && theParamsUri.isEmpty()) {
502                        return;
503                }
504                getParamsUri().clear();
505                getParamsUri().addAll(theParamsUri);
506        }
507
508        @Override
509        public Long getResourceId() {
510                return getId();
511        }
512
513        public Collection<ResourceLink> getResourceLinks() {
514                if (myResourceLinks == null) {
515                        myResourceLinks = new ArrayList<>();
516                }
517                return myResourceLinks;
518        }
519
520        public void setResourceLinks(Collection<ResourceLink> theLinks) {
521                if (!isHasLinks() && theLinks.isEmpty()) {
522                        return;
523                }
524                getResourceLinks().clear();
525                getResourceLinks().addAll(theLinks);
526        }
527
528        @Override
529        public String getResourceType() {
530                return myResourceType;
531        }
532
533        public ResourceTable setResourceType(String theResourceType) {
534                myResourceType = theResourceType;
535                return this;
536        }
537
538        @Override
539        public Collection<ResourceTag> getTags() {
540                if (myTags == null) {
541                        myTags = new HashSet<>();
542                }
543                return myTags;
544        }
545
546        @Override
547        public long getVersion() {
548                return myVersion;
549        }
550
551        @Override
552        public boolean isDeleted() {
553                return getDeleted() != null;
554        }
555
556        @Override
557        public void setNotDeleted() {
558                setDeleted(null);
559        }
560
561        public void setVersion(long theVersion) {
562                myVersion = theVersion;
563        }
564
565        public boolean isHasLinks() {
566                return myHasLinks;
567        }
568
569        public void setHasLinks(boolean theHasLinks) {
570                myHasLinks = theHasLinks;
571        }
572
573        public boolean isParamsComboStringUniquePresent() {
574                if (myParamsComboStringUniquePresent == null) {
575                        return false;
576                }
577                return myParamsComboStringUniquePresent;
578        }
579
580        public void setParamsComboStringUniquePresent(boolean theParamsComboStringUniquePresent) {
581                myParamsComboStringUniquePresent = theParamsComboStringUniquePresent;
582        }
583
584        public boolean isParamsComboTokensNonUniquePresent() {
585                if (myParamsComboTokensNonUniquePresent == null) {
586                        return false;
587                }
588                return myParamsComboTokensNonUniquePresent;
589        }
590
591        public void setParamsComboTokensNonUniquePresent(boolean theParamsComboTokensNonUniquePresent) {
592                myParamsComboStringUniquePresent = theParamsComboTokensNonUniquePresent;
593        }
594
595        public boolean isParamsCoordsPopulated() {
596                return myParamsCoordsPopulated;
597        }
598
599        public void setParamsCoordsPopulated(boolean theParamsCoordsPopulated) {
600                myParamsCoordsPopulated = theParamsCoordsPopulated;
601        }
602
603        public boolean isParamsDatePopulated() {
604                return myParamsDatePopulated;
605        }
606
607        public void setParamsDatePopulated(boolean theParamsDatePopulated) {
608                myParamsDatePopulated = theParamsDatePopulated;
609        }
610
611        public boolean isParamsNumberPopulated() {
612                return myParamsNumberPopulated;
613        }
614
615        public void setParamsNumberPopulated(boolean theParamsNumberPopulated) {
616                myParamsNumberPopulated = theParamsNumberPopulated;
617        }
618
619        public boolean isParamsQuantityPopulated() {
620                return myParamsQuantityPopulated;
621        }
622
623        public void setParamsQuantityPopulated(boolean theParamsQuantityPopulated) {
624                myParamsQuantityPopulated = theParamsQuantityPopulated;
625        }
626        
627        public Boolean isParamsQuantityNormalizedPopulated() {
628                if (myParamsQuantityNormalizedPopulated == null)
629                        return Boolean.FALSE;
630                else
631                        return myParamsQuantityNormalizedPopulated;
632        }
633
634        public void setParamsQuantityNormalizedPopulated(Boolean theParamsQuantityNormalizedPopulated) {
635                if (theParamsQuantityNormalizedPopulated == null)
636                        myParamsQuantityNormalizedPopulated = Boolean.FALSE;
637                else
638                        myParamsQuantityNormalizedPopulated = theParamsQuantityNormalizedPopulated;
639        }
640
641        public boolean isParamsStringPopulated() {
642                return myParamsStringPopulated;
643        }
644
645        public void setParamsStringPopulated(boolean theParamsStringPopulated) {
646                myParamsStringPopulated = theParamsStringPopulated;
647        }
648
649        public boolean isParamsTokenPopulated() {
650                return myParamsTokenPopulated;
651        }
652
653        public void setParamsTokenPopulated(boolean theParamsTokenPopulated) {
654                myParamsTokenPopulated = theParamsTokenPopulated;
655        }
656
657        public boolean isParamsUriPopulated() {
658                return myParamsUriPopulated;
659        }
660
661        public void setParamsUriPopulated(boolean theParamsUriPopulated) {
662                myParamsUriPopulated = theParamsUriPopulated;
663        }
664
665        /**
666         * Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
667         * and was not re-saved in the database
668         */
669        public boolean isUnchangedInCurrentOperation() {
670                return myUnchangedInCurrentOperation;
671        }
672
673        /**
674         * Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
675         * and was not re-saved in the database
676         */
677        public void setUnchangedInCurrentOperation(boolean theUnchangedInCurrentOperation) {
678
679                myUnchangedInCurrentOperation = theUnchangedInCurrentOperation;
680        }
681
682        public void setContentText(String theContentText) {
683                myContentText = theContentText;
684        }
685
686        public String getContentText() {
687                return myContentText;
688        }
689
690        public void setNarrativeText(String theNarrativeText) {
691                myNarrativeText = theNarrativeText;
692        }
693
694        public ResourceHistoryTable toHistory(boolean theCreateVersionTags) {
695                ResourceHistoryTable retVal = new ResourceHistoryTable();
696
697                retVal.setResourceId(myId);
698                retVal.setResourceType(myResourceType);
699                retVal.setVersion(myVersion);
700                retVal.setTransientForcedId(getTransientForcedId());
701
702                retVal.setPublished(getPublishedDate());
703                retVal.setUpdated(getUpdatedDate());
704                retVal.setFhirVersion(getFhirVersion());
705                retVal.setDeleted(getDeleted());
706                retVal.setResourceTable(this);
707                retVal.setForcedId(getForcedId());
708                retVal.setPartitionId(getPartitionId());
709
710                retVal.getTags().clear();
711
712                retVal.setHasTags(isHasTags());
713                if (isHasTags() && theCreateVersionTags) {
714                        for (ResourceTag next : getTags()) {
715                                retVal.addTag(next);
716                        }
717                }
718
719                return retVal;
720        }
721
722        @Override
723        public String toString() {
724                ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
725                b.append("pid", myId);
726                b.append("resourceType", myResourceType);
727                b.append("version", myVersion);
728                if (getPartitionId() != null) {
729                        b.append("partitionId", getPartitionId().getPartitionId());
730                }
731                b.append("lastUpdated", getUpdated().getValueAsString());
732                if (getDeleted() != null) {
733                        b.append("deleted");
734                }
735                return b.build();
736        }
737
738        @PrePersist
739        @PreUpdate
740        public void preSave() {
741                if (myHasLinks && myResourceLinks != null) {
742                        myResourceLinksField = getResourceLinks()
743                                .stream()
744                                .map(ResourceLink::getTargetResourcePid)
745                                .filter(Objects::nonNull)
746                                .map(Object::toString)
747                                .collect(Collectors.joining(" "));
748                } else {
749                        myResourceLinksField = null;
750                }
751        }
752
753        /**
754         * This is a convenience to avoid loading the version a second time within a single transaction. It is
755         * not persisted.
756         */
757        public void setCurrentVersionEntity(ResourceHistoryTable theCurrentVersionEntity) {
758                myCurrentVersionEntity = theCurrentVersionEntity;
759        }
760
761        /**
762         * This is a convenience to avoid loading the version a second time within a single transaction. It is
763         * not persisted.
764         */
765        public ResourceHistoryTable getCurrentVersionEntity() {
766                return myCurrentVersionEntity;
767        }
768
769        @Override
770        public ResourcePersistentId getPersistentId() {
771                return new ResourcePersistentId(getId());
772        }
773
774        @Override
775        public ForcedId getForcedId() {
776                return myForcedId;
777        }
778
779        @Override
780        public void setForcedId(ForcedId theForcedId) {
781                myForcedId = theForcedId;
782        }
783
784
785
786        @Override
787        public IdDt getIdDt() {
788                IdDt retVal = new IdDt();
789                populateId(retVal);
790                return retVal;
791        }
792
793
794        public IIdType getIdType(FhirContext theContext) {
795                IIdType retVal = theContext.getVersion().newIdType();
796                populateId(retVal);
797                return retVal;
798        }
799
800        private void populateId(IIdType retVal) {
801                if (getTransientForcedId() != null) {
802                        // Avoid a join query if possible
803                        retVal.setValue(getResourceType() + '/' + getTransientForcedId() + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
804                } else if (getForcedId() == null) {
805                        Long id = this.getResourceId();
806                        retVal.setValue(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
807                } else {
808                        String forcedId = getForcedId().getForcedId();
809                        retVal.setValue(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
810                }
811        }
812
813        public void setCreatedByMatchUrl(String theCreatedByMatchUrl) {
814                myCreatedByMatchUrl = theCreatedByMatchUrl;
815        }
816
817        public String getCreatedByMatchUrl() {
818                return myCreatedByMatchUrl;
819        }
820
821        public void setLuceneIndexData(ExtendedHSearchIndexData theLuceneIndexData) {
822                myLuceneIndexData = theLuceneIndexData;
823        }
824
825        public Collection<SearchParamPresentEntity> getSearchParamPresents() {
826                if (mySearchParamPresents == null) {
827                        mySearchParamPresents = new ArrayList<>();
828                }
829                return mySearchParamPresents;
830        }
831
832        /**
833         * Get the FHIR resource id.
834         *
835         * @return the resource id, or null if the resource doesn't have a client-assigned id,
836         * and hasn't been saved to the db to get a server-assigned id yet.
837         */
838        public String getFhirId() {
839                return myFhirId;
840        }
841
842        public void setFhirId(String theFhirId) {
843                myFhirId = theFhirId;
844        }
845}