001/*-
002 * #%L
003 * HAPI FHIR JPA Model
004 * %%
005 * Copyright (C) 2014 - 2024 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.model.entity;
021
022import ca.uhn.fhir.context.ParserOptions;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.jpa.model.config.PartitionSettings;
025import ca.uhn.fhir.jpa.util.ISequenceValueMassager;
026import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
027import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc;
028import ca.uhn.fhir.util.HapiExtensions;
029import org.apache.commons.lang3.Validate;
030import org.hl7.fhir.instance.model.api.IPrimitiveType;
031import org.hl7.fhir.r4.model.DateTimeType;
032
033import java.util.Arrays;
034import java.util.Collections;
035import java.util.Date;
036import java.util.HashMap;
037import java.util.HashSet;
038import java.util.Map;
039import java.util.Set;
040
041import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
042
043/**
044 * This class contains configuration options common to all hapi-fhir-storage implementations.
045 * Ultimately it should live in that project
046 */
047public class StorageSettings {
048        /**
049         * @since 5.6.0
050         */
051        // Thread Pool size used by batch in bundle
052        public static final int DEFAULT_BUNDLE_BATCH_POOL_SIZE = 20; // 1 for single thread
053
054        public static final int DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE = 100; // 1 for single thread
055        /**
056         * Default {@link #getTreatReferencesAsLogical() logical URL bases}. Includes the following
057         * values:
058         * <ul>
059         * <li><code>"http://hl7.org/fhir/valueset-*"</code></li>
060         * <li><code>"http://hl7.org/fhir/codesystem-*"</code></li>
061         * <li><code>"http://hl7.org/fhir/StructureDefinition/*"</code></li>
062         * </ul>
063         */
064        public static final Set<String> DEFAULT_LOGICAL_BASE_URLS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
065                        "http://hl7.org/fhir/ValueSet/*",
066                        "http://hl7.org/fhir/CodeSystem/*",
067                        "http://hl7.org/fhir/valueset-*",
068                        "http://hl7.org/fhir/codesystem-*",
069                        "http://hl7.org/fhir/StructureDefinition/*")));
070
071        /*
072         * <p>
073         * Note the following database documented limitations:
074         *    <ul>
075         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
076         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
077         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
078         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
079         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
080         *     </ul>
081         * </p>
082         */
083        protected static final String DEFAULT_PERIOD_INDEX_START_OF_TIME = "1001-01-01";
084        protected static final String DEFAULT_PERIOD_INDEX_END_OF_TIME = "9000-01-01";
085        private static final Integer DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE = null;
086        /**
087         * update setter javadoc if default changes
088         */
089        private boolean myAllowContainsSearches = false;
090
091        private boolean myAllowExternalReferences = false;
092        private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>();
093        private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS);
094        private boolean myDefaultSearchParamsCanBeOverridden = true;
095        private boolean myAutoCreatePlaceholderReferenceTargets;
096        private Integer myBundleBatchPoolSize = DEFAULT_BUNDLE_BATCH_POOL_SIZE;
097        private Integer myBundleBatchMaxPoolSize = DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE;
098        private boolean myMassIngestionMode;
099        private Integer myMaximumTransactionBundleSize = DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE;
100        private boolean myNormalizeTerminologyForBulkExportJobs = false;
101        /**
102         * Update setter javadoc if default changes.
103         */
104        private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
105
106        private boolean mySuppressStringIndexingInTokens = false;
107        private Class<? extends ISequenceValueMassager> mySequenceValueMassagerClass;
108        private IPrimitiveType<Date> myPeriodIndexStartOfTime;
109        private IPrimitiveType<Date> myPeriodIndexEndOfTime;
110        private NormalizedQuantitySearchLevel myNormalizedQuantitySearchLevel;
111        private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
112        private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
113        private boolean myRespectVersionsForSearchIncludes;
114        private boolean myIndexOnUpliftedRefchains = false;
115        private boolean myIndexOnContainedResources = false;
116        private boolean myIndexOnContainedResourcesRecursively = false;
117        private boolean myAllowMdmExpansion = false;
118        private boolean myAutoSupportDefaultSearchParams = true;
119        private boolean myIndexIdentifierOfType = false;
120        private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
121
122        /**
123         * Should the {@literal _lamguage} SearchParameter be supported
124         * on this server?
125         *
126         * @since 7.0.0
127         */
128        private boolean myLanguageSearchParameterEnabled = false;
129
130        /**
131         * If set to false, all resource types will be installed via package installer, regardless of their status.
132         * Otherwise, resources will be filtered based on status according to some criteria which can be found in
133         * <code>PackageInstallerSvcImpl#isValidResourceStatusForPackageUpload<code>
134         * @since 7.0.0
135         */
136        private boolean myValidateResourceStatusForPackageUpload = true;
137
138        /**
139         * If set to <code>true</code>, the server will not write data to the <code>SP_NAME, RES_TYPE, SP_UPDATED</code>
140         * columns for all HFJ_SPIDX tables.
141         *
142         * @since 7.4.0
143         */
144        private boolean myIndexStorageOptimized = false;
145
146        /**
147         * Constructor
148         */
149        public StorageSettings() {
150                setSequenceValueMassagerClass(ISequenceValueMassager.NoopSequenceValueMassager.class);
151                setPeriodIndexStartOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_START_OF_TIME));
152                setPeriodIndexEndOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_END_OF_TIME));
153                setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
154        }
155
156        /**
157         * When creating or updating a resource: If this property is set to <code>true</code>
158         * (default is <code>false</code>), if the resource has a reference to another resource
159         * on the local server but that reference does not exist, a placeholder resource will be
160         * created.
161         * <p>
162         * In other words, if an observation with subject <code>Patient/FOO</code> is created, but
163         * there is no resource called <code>Patient/FOO</code> on the server, this property causes
164         * an empty patient with ID "FOO" to be created in order to prevent this operation
165         * from failing.
166         * </p>
167         * <p>
168         * This property can be useful in cases where replication between two servers is wanted.
169         * Note however that references containing purely numeric IDs will not be auto-created
170         * as they are never allowed to be client supplied in HAPI FHIR JPA.
171         * <p>
172         * All placeholder resources created in this way have an extension
173         * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
174         * </p>
175         */
176        public boolean isAutoCreatePlaceholderReferenceTargets() {
177                return myAutoCreatePlaceholderReferenceTargets;
178        }
179
180        /**
181         * When creating or updating a resource: If this property is set to <code>true</code>
182         * (default is <code>false</code>), if the resource has a reference to another resource
183         * on the local server but that reference does not exist, a placeholder resource will be
184         * created.
185         * <p>
186         * In other words, if an observation with subject <code>Patient/FOO</code> is created, but
187         * there is no resource called <code>Patient/FOO</code> on the server, this property causes
188         * an empty patient with ID "FOO" to be created in order to prevent this operation
189         * from failing.
190         * </p>
191         * <p>
192         * This property can be useful in cases where replication between two servers is wanted.
193         * Note however that references containing purely numeric IDs will not be auto-created
194         * as they are never allowed to be client supplied in HAPI FHIR JPA.
195         * <p>
196         * All placeholder resources created in this way have an extension
197         * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
198         * </p>
199         */
200        public void setAutoCreatePlaceholderReferenceTargets(boolean theAutoCreatePlaceholderReferenceTargets) {
201                myAutoCreatePlaceholderReferenceTargets = theAutoCreatePlaceholderReferenceTargets;
202        }
203
204        /**
205         * Get the batch transaction thread pool size.
206         *
207         * @since 5.6.0
208         */
209        public Integer getBundleBatchPoolSize() {
210                return myBundleBatchPoolSize;
211        }
212
213        /**
214         * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_POOL_SIZE}
215         * set pool size to 1 for single thread
216         *
217         * @since 5.6.0
218         */
219        public void setBundleBatchPoolSize(Integer theBundleBatchPoolSize) {
220                this.myBundleBatchPoolSize = theBundleBatchPoolSize;
221        }
222
223        /**
224         * Get the batch transaction thread max pool size.
225         * set max pool size to 1 for single thread
226         *
227         * @since 5.6.0
228         */
229        public Integer getBundleBatchMaxPoolSize() {
230                return myBundleBatchMaxPoolSize;
231        }
232
233        /**
234         * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE}
235         *
236         * @since 5.6.0
237         */
238        public void setBundleBatchMaxPoolSize(Integer theBundleBatchMaxPoolSize) {
239                this.myBundleBatchMaxPoolSize = theBundleBatchMaxPoolSize;
240        }
241
242        /**
243         * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
244         * the server will not create search indexes for search parameters with no values in resources.
245         * <p>
246         * Disabling this feature means that the <code>:missing</code> search modifier will not be
247         * supported on the server, but also means that storage and indexing (i.e. writes to the
248         * database) may be much faster on servers which have lots of search parameters and need
249         * to write quickly.
250         * </p>
251         * <p>
252         * This feature may be enabled on servers where supporting the use of the :missing parameter is
253         * of higher importance than raw write performance
254         * </p>
255         */
256        public IndexEnabledEnum getIndexMissingFields() {
257                return myIndexMissingFieldsEnabled;
258        }
259
260        /**
261         * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
262         * the server will not create search indexes for search parameters with no values in resources.
263         * <p>
264         * Disabling this feature means that the <code>:missing</code> search modifier will not be
265         * supported on the server, but also means that storage and indexing (i.e. writes to the
266         * database) may be much faster on servers which have lots of search parameters and need
267         * to write quickly.
268         * </p>
269         * <p>
270         * This feature may be enabled on servers where supporting the use of the :missing parameter is
271         * of higher importance than raw write performance
272         * </p>
273         * <p>
274         * Note that this setting also has an impact on sorting (i.e. using the
275         * <code>_sort</code> parameter on searches): If the server is configured
276         * to not index missing field.
277         * </p>
278         * <p>
279         * The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code>
280         * to improve the search performance while <code>:missing</code> is enabled.
281         * <code>RES_TYPE, SP_NAME, SP_MISSING</code>
282         * </p>
283         */
284        public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
285                Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
286                myIndexMissingFieldsEnabled = theIndexMissingFields;
287        }
288
289        /**
290         * If set to <code>true</code> (default is false), the server will not write data
291         * to the <code>SP_NAME, RES_TYPE, SP_UPDATED</code> columns for all HFJ_SPIDX tables.
292         * <p>
293         * This feature may be enabled on servers where HFJ_SPIDX tables are expected
294         * to have a large amount of data (millions of rows) in order to reduce overall storage size.
295         * </p>
296         * <p>
297         * Note that this setting only applies to newly inserted and updated rows in HFJ_SPIDX tables.
298         * In order to apply this optimization setting to existing HFJ_SPIDX index rows,
299         * <code>$reindex</code> operation should be executed at the instance or server level.
300         * <p>
301         * <p>
302         * If this setting is enabled, {@link PartitionSettings#isIncludePartitionInSearchHashes()} should be disabled.
303         * </p>
304         * <p>
305         * If {@link StorageSettings#getIndexMissingFields()} is enabled, the following index may need to be added
306         * into the HFJ_SPIDX tables to improve the search performance: <code>HASH_IDENTITY, SP_MISSING, RES_ID, PARTITION_ID</code>
307         * </p>
308         *
309         * @since 7.4.0
310         */
311        public boolean isIndexStorageOptimized() {
312                return myIndexStorageOptimized;
313        }
314
315        /**
316         * If set to <code>true</code> (default is false), the server will not write data
317         * to the <code>SP_NAME, RES_TYPE, SP_UPDATED</code> columns for all HFJ_SPIDX tables.
318         * <p>
319         * This feature may be enabled on servers where HFJ_SPIDX tables are expected
320         * to have a large amount of data (millions of rows) in order to reduce overall storage size.
321         * </p>
322         * <p>
323         * Note that this setting only applies to newly inserted and updated rows in HFJ_SPIDX tables.
324         * In order to apply this optimization setting to existing HFJ_SPIDX index rows,
325         * <code>$reindex</code> operation should be executed at the instance or server level.
326         * <p>
327         * <p>
328         * If this setting is enabled, {@link PartitionSettings#isIncludePartitionInSearchHashes()} should be set to <code>false</code>.
329         * </p>
330         * <p>
331         * If {@link StorageSettings#getIndexMissingFields()} ()} is enabled, the following index may need to be added
332         * into the HFJ_SPIDX tables to improve the search performance: <code>HASH_IDENTITY, SP_MISSING, RES_ID, PARTITION_ID</code>
333         * </p>
334         *
335         * @since 7.4.0
336         */
337        public void setIndexStorageOptimized(boolean theIndexStorageOptimized) {
338                myIndexStorageOptimized = theIndexStorageOptimized;
339        }
340
341        /**
342         * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of
343         * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not
344         * being otherwise used.
345         * <p>
346         * In this mode:
347         * <p>
348         * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them
349         * - Resources modification checks will be skipped in favour of a simple hash check
350         * - Extra resource ID caching is enabled
351         *
352         * @since 5.5.0
353         */
354        public boolean isMassIngestionMode() {
355                return myMassIngestionMode;
356        }
357
358        /**
359         * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of
360         * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not
361         * being otherwise used.
362         * <p>
363         * In this mode:
364         * <p>
365         * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them
366         * - Resources modification checks will be skipped in favour of a simple hash check
367         * - Extra resource ID caching is enabled
368         *
369         * @since 5.5.0
370         */
371        public void setMassIngestionMode(boolean theMassIngestionMode) {
372                myMassIngestionMode = theMassIngestionMode;
373        }
374
375        /**
376         * Specifies the maximum number of resources permitted within a single transaction bundle.
377         * If a transaction bundle is submitted with more than this number of resources, it will be
378         * rejected with a PayloadTooLarge exception.
379         * <p>
380         * The default value is <code>null</code>, which means that there is no limit.
381         * </p>
382         */
383        public Integer getMaximumTransactionBundleSize() {
384                return myMaximumTransactionBundleSize;
385        }
386
387        /**
388         * Specifies the maximum number of resources permitted within a single transaction bundle.
389         * If a transaction bundle is submitted with more than this number of resources, it will be
390         * rejected with a PayloadTooLarge exception.
391         * <p>
392         * The default value is <code>null</code>, which means that there is no limit.
393         * </p>
394         */
395        public StorageSettings setMaximumTransactionBundleSize(Integer theMaximumTransactionBundleSize) {
396                myMaximumTransactionBundleSize = theMaximumTransactionBundleSize;
397                return this;
398        }
399
400        /**
401         * If set to true, attempt to map terminology for bulk export jobs using the
402         * logic in
403         * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
404         *
405         * @since 6.3.0
406         */
407        public boolean isNormalizeTerminologyForBulkExportJobs() {
408                return myNormalizeTerminologyForBulkExportJobs;
409        }
410
411        /**
412         * If set to true, attempt to map terminology for bulk export jobs using the
413         * logic in
414         * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
415         *
416         * @since 6.3.0
417         */
418        public void setNormalizeTerminologyForBulkExportJobs(boolean theNormalizeTerminologyForBulkExportJobs) {
419                myNormalizeTerminologyForBulkExportJobs = theNormalizeTerminologyForBulkExportJobs;
420        }
421
422        /**
423         * This is an internal API and may change or disappear without notice
424         *
425         * @since 6.2.0
426         */
427        public Class<? extends ISequenceValueMassager> getSequenceValueMassagerClass() {
428                return mySequenceValueMassagerClass;
429        }
430
431        /**
432         * This is an internal API and may change or disappear without notice
433         *
434         * @since 6.2.0
435         */
436        public void setSequenceValueMassagerClass(Class<? extends ISequenceValueMassager> theSequenceValueMassagerClass) {
437                Validate.notNull(theSequenceValueMassagerClass, "theSequenceValueMassagerClass must not be null");
438                mySequenceValueMassagerClass = theSequenceValueMassagerClass;
439        }
440
441        /**
442         * If set to <code>true</code> (default is <code>false</code>) the
443         * <code>:of-type</code> modifier on token search parameters for
444         * identifiers will be supported. Enabling this causes additional
445         * indexing overhead (although very minor) so it is disabled unless it is
446         * actually needed.
447         *
448         * @since 5.7.0
449         */
450        public boolean isIndexIdentifierOfType() {
451                return myIndexIdentifierOfType;
452        }
453
454        /**
455         * If set to <code>true</code> (default is <code>false</code>) the
456         * <code>:of-type</code> modifier on token search parameters for
457         * identifiers will be supported. Enabling this causes additional
458         * indexing overhead (although very minor) so it is disabled unless it is
459         * actually needed.
460         *
461         * @since 5.7.0
462         */
463        public void setIndexIdentifierOfType(boolean theIndexIdentifierOfType) {
464                myIndexIdentifierOfType = theIndexIdentifierOfType;
465        }
466
467        /**
468         * If set to {@code true} the default search params (i.e. the search parameters that are
469         * defined by the FHIR specification itself) may be overridden by uploading search
470         * parameters to the server with the same code as the built-in search parameter.
471         * <p>
472         * This can be useful if you want to be able to disable or alter
473         * the behaviour of the default search parameters.
474         * </p>
475         * <p>
476         * The default value for this setting is {@code true}
477         * </p>
478         */
479        public boolean isDefaultSearchParamsCanBeOverridden() {
480                return myDefaultSearchParamsCanBeOverridden;
481        }
482
483        /**
484         * If set to {@code true} the default search params (i.e. the search parameters that are
485         * defined by the FHIR specification itself) may be overridden by uploading search
486         * parameters to the server with the same code as the built-in search parameter.
487         * <p>
488         * This can be useful if you want to be able to disable or alter
489         * the behaviour of the default search parameters.
490         * </p>
491         * <p>
492         * The default value for this setting is {@code true}
493         * </p>
494         */
495        public void setDefaultSearchParamsCanBeOverridden(boolean theDefaultSearchParamsCanBeOverridden) {
496                myDefaultSearchParamsCanBeOverridden = theDefaultSearchParamsCanBeOverridden;
497        }
498
499        /**
500         * If enabled, the server will support the use of :contains searches,
501         * which are helpful but can have adverse effects on performance.
502         * <p>
503         * Default is <code>false</code> (Note that prior to HAPI FHIR
504         * 3.5.0 the default was <code>true</code>)
505         * </p>
506         * <p>
507         * Note: If you change this value after data already has
508         * already been stored in the database, you must for a reindexing
509         * of all data in the database or resources may not be
510         * searchable.
511         * </p>
512         */
513        public boolean isAllowContainsSearches() {
514                return myAllowContainsSearches;
515        }
516
517        /**
518         * If enabled, the server will support the use of :contains searches,
519         * which are helpful but can have adverse effects on performance.
520         * <p>
521         * Default is <code>false</code> (Note that prior to HAPI FHIR
522         * 3.5.0 the default was <code>true</code>)
523         * </p>
524         * <p>
525         * Note: If you change this value after data already has
526         * already been stored in the database, you must for a reindexing
527         * of all data in the database or resources may not be
528         * searchable.
529         * </p>
530         */
531        public void setAllowContainsSearches(boolean theAllowContainsSearches) {
532                this.myAllowContainsSearches = theAllowContainsSearches;
533        }
534
535        /**
536         * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters.
537         * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier
538         * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1
539         * is MDM-matched to Patient/2 and you execute the search:
540         * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2.
541         * <p>
542         * Default is <code>false</code>
543         * </p>
544         *
545         * @since 5.4.0
546         */
547        public boolean isAllowMdmExpansion() {
548                return myAllowMdmExpansion;
549        }
550
551        /**
552         * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters.
553         * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier
554         * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1
555         * is MDM-matched to Patient/2 and you execute the search:
556         * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2.
557         * <p>
558         * Default is <code>false</code>
559         * </p>
560         *
561         * @since 5.4.0
562         */
563        public void setAllowMdmExpansion(boolean theAllowMdmExpansion) {
564                myAllowMdmExpansion = theAllowMdmExpansion;
565        }
566
567        /**
568         * If set to <code>true</code> (default is <code>false</code>) the server will allow
569         * resources to have references to external servers. For example if this server is
570         * running at <code>http://example.com/fhir</code> and this setting is set to
571         * <code>true</code> the server will allow a Patient resource to be saved with a
572         * Patient.organization value of <code>http://foo.com/Organization/1</code>.
573         * <p>
574         * Under the default behaviour if this value has not been changed, the above
575         * resource would be rejected by the server because it requires all references
576         * to be resolvable on the local server.
577         * </p>
578         * <p>
579         * Note that external references will be indexed by the server and may be searched
580         * (e.g. <code>Patient:organization</code>), but
581         * chained searches (e.g. <code>Patient:organization.name</code>) will not work across
582         * these references.
583         * </p>
584         * <p>
585         * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
586         * is set to <code>true</code>
587         * </p>
588         *
589         * @see #setTreatBaseUrlsAsLocal(Set)
590         * @see #setAllowExternalReferences(boolean)
591         */
592        public boolean isAllowExternalReferences() {
593                return myAllowExternalReferences;
594        }
595
596        /**
597         * If set to <code>true</code> (default is <code>false</code>) the server will allow
598         * resources to have references to external servers. For example if this server is
599         * running at <code>http://example.com/fhir</code> and this setting is set to
600         * <code>true</code> the server will allow a Patient resource to be saved with a
601         * Patient.organization value of <code>http://foo.com/Organization/1</code>.
602         * <p>
603         * Under the default behaviour if this value has not been changed, the above
604         * resource would be rejected by the server because it requires all references
605         * to be resolvable on the local server.
606         * </p>
607         * <p>
608         * Note that external references will be indexed by the server and may be searched
609         * (e.g. <code>Patient:organization</code>), but
610         * chained searches (e.g. <code>Patient:organization.name</code>) will not work across
611         * these references.
612         * </p>
613         * <p>
614         * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
615         * is set to <code>true</code>
616         * </p>
617         *
618         * @see #setTreatBaseUrlsAsLocal(Set)
619         * @see #setAllowExternalReferences(boolean)
620         */
621        public void setAllowExternalReferences(boolean theAllowExternalReferences) {
622                myAllowExternalReferences = theAllowExternalReferences;
623        }
624
625        /**
626         * This setting may be used to advise the server that any references found in
627         * resources that have any of the base URLs given here will be replaced with
628         * simple local references.
629         * <p>
630         * For example, if the set contains the value <code>http://example.com/base/</code>
631         * and a resource is submitted to the server that contains a reference to
632         * <code>http://example.com/base/Patient/1</code>, the server will automatically
633         * convert this reference to <code>Patient/1</code>
634         * </p>
635         * <p>
636         * Note that this property has different behaviour from {@link StorageSettings#getTreatReferencesAsLogical()}
637         * </p>
638         *
639         * @see #getTreatReferencesAsLogical()
640         */
641        public Set<String> getTreatBaseUrlsAsLocal() {
642                return myTreatBaseUrlsAsLocal;
643        }
644
645        /**
646         * This setting may be used to advise the server that any references found in
647         * resources that have any of the base URLs given here will be replaced with
648         * simple local references.
649         * <p>
650         * For example, if the set contains the value <code>http://example.com/base/</code>
651         * and a resource is submitted to the server that contains a reference to
652         * <code>http://example.com/base/Patient/1</code>, the server will automatically
653         * convert this reference to <code>Patient/1</code>
654         * </p>
655         *
656         * @param theTreatBaseUrlsAsLocal The set of base URLs. May be <code>null</code>, which
657         *                                means no references will be treated as external
658         */
659        public void setTreatBaseUrlsAsLocal(Set<String> theTreatBaseUrlsAsLocal) {
660                if (theTreatBaseUrlsAsLocal != null) {
661                        for (String next : theTreatBaseUrlsAsLocal) {
662                                validateTreatBaseUrlsAsLocal(next);
663                        }
664                }
665
666                HashSet<String> treatBaseUrlsAsLocal = new HashSet<>();
667                for (String next : defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet<String>())) {
668                        while (next.endsWith("/")) {
669                                next = next.substring(0, next.length() - 1);
670                        }
671                        treatBaseUrlsAsLocal.add(next);
672                }
673                myTreatBaseUrlsAsLocal = treatBaseUrlsAsLocal;
674        }
675
676        /**
677         * Add a value to the {@link #setTreatReferencesAsLogical(Set) logical references list}.
678         *
679         * @see #setTreatReferencesAsLogical(Set)
680         */
681        public void addTreatReferencesAsLogical(String theTreatReferencesAsLogical) {
682                validateTreatBaseUrlsAsLocal(theTreatReferencesAsLogical);
683
684                if (myTreatReferencesAsLogical == null) {
685                        myTreatReferencesAsLogical = new HashSet<>();
686                }
687                myTreatReferencesAsLogical.add(theTreatReferencesAsLogical);
688        }
689
690        /**
691         * This setting may be used to advise the server that any references found in
692         * resources that have any of the base URLs given here will be treated as logical
693         * references instead of being treated as real references.
694         * <p>
695         * A logical reference is a reference which is treated as an identifier, and
696         * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for
697         * a description of logical references. For example, the valueset
698         * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical
699         * reference.
700         * </p>
701         * <p>
702         * Values for this field may take either of the following forms:
703         * </p>
704         * <ul>
705         * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li>
706         * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
707         * </ul>
708         *
709         * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
710         */
711        public Set<String> getTreatReferencesAsLogical() {
712                return myTreatReferencesAsLogical;
713        }
714
715        /**
716         * This setting may be used to advise the server that any references found in
717         * resources that have any of the base URLs given here will be treated as logical
718         * references instead of being treated as real references.
719         * <p>
720         * A logical reference is a reference which is treated as an identifier, and
721         * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for
722         * a description of logical references. For example, the valueset
723         * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical
724         * reference.
725         * </p>
726         * <p>
727         * Values for this field may take either of the following forms:
728         * </p>
729         * <ul>
730         * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li>
731         * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
732         * </ul>
733         *
734         * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
735         */
736        public StorageSettings setTreatReferencesAsLogical(Set<String> theTreatReferencesAsLogical) {
737                myTreatReferencesAsLogical = theTreatReferencesAsLogical;
738                return this;
739        }
740
741        /**
742         * <p>
743         * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
744         * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
745         * precision of {@link TemporalPrecisionEnum#DAY}.
746         * <p>
747         * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
748         * integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
749         * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
750         * </p>
751         * Default is {@literal true} beginning in HAPI FHIR 5.0.0
752         * </p>
753         *
754         * @since 5.0.0
755         */
756        public boolean getUseOrdinalDatesForDayPrecisionSearches() {
757                return myUseOrdinalDatesForDayPrecisionSearches;
758        }
759
760        /**
761         * <p>
762         * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
763         * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
764         * precision of {@link TemporalPrecisionEnum#DAY}.
765         * <p>
766         * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
767         * ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
768         * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
769         * </p>
770         * Default is {@literal true} beginning in HAPI FHIR 5.0.0
771         * </p>
772         *
773         * @since 5.0.0
774         */
775        public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
776                myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
777        }
778
779        /**
780         * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
781         * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
782         * will not be indexed for tokens:
783         * <ul>
784         *    <li>CodeableConcept.text</li>
785         *    <li>Coding.display</li>
786         *    <li>Identifier.use.text</li>
787         * </ul>
788         *
789         * @since 5.0.0
790         */
791        public boolean isSuppressStringIndexingInTokens() {
792                return mySuppressStringIndexingInTokens;
793        }
794
795        /**
796         * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
797         * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
798         * will not be indexed for tokens:
799         * <ul>
800         *    <li>CodeableConcept.text</li>
801         *    <li>Coding.display</li>
802         *    <li>Identifier.use.text</li>
803         * </ul>
804         *
805         * @since 5.0.0
806         */
807        public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) {
808                mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens;
809        }
810
811        /**
812         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
813         * but not a lower bound, a canned "start of time" value can be used as the lower bound
814         * in order to allow range searches to correctly identify all values in the range.
815         * <p>
816         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
817         * is probably good enough for almost any application, but this can be changed if
818         * needed.
819         * </p>
820         * <p>
821         * Note the following database documented limitations:
822         *    <ul>
823         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
824         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
825         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
826         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
827         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
828         *     </ul>
829         * </p>
830         *
831         * @see #getPeriodIndexEndOfTime()
832         * @since 5.1.0
833         */
834        public IPrimitiveType<Date> getPeriodIndexStartOfTime() {
835                return myPeriodIndexStartOfTime;
836        }
837
838        /**
839         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
840         * but not a lower bound, a canned "start of time" value can be used as the lower bound
841         * in order to allow range searches to correctly identify all values in the range.
842         * <p>
843         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
844         * is probably good enough for almost any application, but this can be changed if
845         * needed.
846         * </p>
847         * <p>
848         * Note the following database documented limitations:
849         *    <ul>
850         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
851         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
852         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
853         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
854         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
855         *     </ul>
856         * </p>
857         *
858         * @see #getPeriodIndexEndOfTime()
859         * @since 5.1.0
860         */
861        public void setPeriodIndexStartOfTime(IPrimitiveType<Date> thePeriodIndexStartOfTime) {
862                Validate.notNull(thePeriodIndexStartOfTime, "thePeriodIndexStartOfTime must not be null");
863                myPeriodIndexStartOfTime = thePeriodIndexStartOfTime;
864        }
865
866        /**
867         * When indexing a Period (e.g. Encounter.period) where the period has a lower bound
868         * but not an upper bound, a canned "end of time" value can be used as the upper bound
869         * in order to allow range searches to correctly identify all values in the range.
870         * <p>
871         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
872         * is probably good enough for almost any application, but this can be changed if
873         * needed.
874         * </p>
875         * <p>
876         * Note the following database documented limitations:
877         *    <ul>
878         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
879         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
880         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
881         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
882         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
883         *     </ul>
884         * </p>
885         *
886         * @see #getPeriodIndexStartOfTime()
887         * @since 5.1.0
888         */
889        public IPrimitiveType<Date> getPeriodIndexEndOfTime() {
890                return myPeriodIndexEndOfTime;
891        }
892
893        /**
894         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
895         * but not a lower bound, a canned "start of time" value can be used as the lower bound
896         * in order to allow range searches to correctly identify all values in the range.
897         * <p>
898         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
899         * is probably good enough for almost any application, but this can be changed if
900         * needed.
901         * </p>
902         * <p>
903         * Note the following database documented limitations:
904         *    <ul>
905         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
906         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
907         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
908         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
909         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
910         *     </ul>
911         * </p>
912         *
913         * @see #getPeriodIndexStartOfTime()
914         * @since 5.1.0
915         */
916        public void setPeriodIndexEndOfTime(IPrimitiveType<Date> thePeriodIndexEndOfTime) {
917                Validate.notNull(thePeriodIndexEndOfTime, "thePeriodIndexEndOfTime must not be null");
918                myPeriodIndexEndOfTime = thePeriodIndexEndOfTime;
919        }
920
921        /**
922         * Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
923         *
924         * <p>
925         * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
926         * </p>
927         * <p>
928         * Here is the UCUM service support level
929         *    <ul>
930         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
931         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
932         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
933         *     </ul>
934         * </p>
935         *
936         * @since 5.3.0
937         */
938        public NormalizedQuantitySearchLevel getNormalizedQuantitySearchLevel() {
939                return myNormalizedQuantitySearchLevel;
940        }
941
942        /**
943         * Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
944         *
945         * <p>
946         * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
947         * </p>
948         * <p>
949         * Here is the UCUM service support level
950         *    <ul>
951         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
952         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
953         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
954         *     </ul>
955         * </p>
956         *
957         * @since 5.3.0
958         */
959        public void setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel theNormalizedQuantitySearchLevel) {
960                myNormalizedQuantitySearchLevel = theNormalizedQuantitySearchLevel;
961        }
962
963        /**
964         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
965         * will automatically have versions appended. The version used will be the current version of the given resource.
966         *
967         * @since 5.3.0
968         */
969        public Set<String> getAutoVersionReferenceAtPaths() {
970                return myAutoVersionReferenceAtPaths;
971        }
972
973        /**
974         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
975         * will automatically have versions appended. The version used will be the current version of the given resource.
976         * <p>
977         * Versions will only be added if the reference does not already have a version, so any versioned references
978         * supplied by the client will take precedence over the automatic current version.
979         * </p>
980         * <p>
981         * Note that for this setting to be useful, the {@link ParserOptions}
982         * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths}
983         * option must also be set.
984         * </p>
985         *
986         * @param thePaths A collection of reference paths for which the versions will be appended automatically
987         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
988         *                 only resource name and field names with dots separating is allowed here (no repetition
989         *                 indicators, FluentPath expressions, etc.)
990         * @since 5.3.0
991         */
992        public void setAutoVersionReferenceAtPaths(String... thePaths) {
993                Set<String> paths = Collections.emptySet();
994                if (thePaths != null) {
995                        paths = new HashSet<>(Arrays.asList(thePaths));
996                }
997                setAutoVersionReferenceAtPaths(paths);
998        }
999
1000        /**
1001         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
1002         * will automatically have versions appended. The version used will be the current version of the given resource.
1003         * <p>
1004         * Versions will only be added if the reference does not already have a version, so any versioned references
1005         * supplied by the client will take precedence over the automatic current version.
1006         * </p>
1007         * <p>
1008         * Note that for this setting to be useful, the {@link ParserOptions}
1009         * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths}
1010         * option must also be set
1011         * </p>
1012         *
1013         * @param thePaths A collection of reference paths for which the versions will be appended automatically
1014         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
1015         *                 only resource name and field names with dots separating is allowed here (no repetition
1016         *                 indicators, FluentPath expressions, etc.)
1017         * @since 5.3.0
1018         */
1019        public void setAutoVersionReferenceAtPaths(Set<String> thePaths) {
1020                Set<String> paths = defaultIfNull(thePaths, Collections.emptySet());
1021                Map<String, Set<String>> byType = new HashMap<>();
1022                for (String nextPath : paths) {
1023                        int doxIdx = nextPath.indexOf('.');
1024                        Validate.isTrue(doxIdx > 0, "Invalid path for auto-version reference at path: %s", nextPath);
1025                        String type = nextPath.substring(0, doxIdx);
1026                        byType.computeIfAbsent(type, t -> new HashSet<>()).add(nextPath);
1027                }
1028
1029                myAutoVersionReferenceAtPaths = paths;
1030                myTypeToAutoVersionReferenceAtPaths = byType;
1031        }
1032
1033        /**
1034         * Returns a sub-collection of {@link #getAutoVersionReferenceAtPaths()} containing only paths
1035         * for the given resource type.
1036         *
1037         * @since 5.3.0
1038         */
1039        public Set<String> getAutoVersionReferenceAtPathsByResourceType(String theResourceType) {
1040                Validate.notEmpty(theResourceType, "theResourceType must not be null or empty");
1041                Set<String> retVal = myTypeToAutoVersionReferenceAtPaths.get(theResourceType);
1042                retVal = defaultIfNull(retVal, Collections.emptySet());
1043                return retVal;
1044        }
1045
1046        /**
1047         * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version.
1048         * This may have performance impacts on heavily loaded systems.
1049         *
1050         * @since 5.3.0
1051         */
1052        public boolean isRespectVersionsForSearchIncludes() {
1053                return myRespectVersionsForSearchIncludes;
1054        }
1055
1056        /**
1057         * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version.
1058         * This may have performance impacts on heavily loaded systems.
1059         *
1060         * @since 5.3.0
1061         */
1062        public void setRespectVersionsForSearchIncludes(boolean theRespectVersionsForSearchIncludes) {
1063                myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
1064        }
1065
1066        /**
1067         * If enabled, "Uplifted Refchains" will be enabled. This feature causes
1068         * HAPI FHIR to generate indexes for stored resources that include the current
1069         * value of the target of a chained reference, such as "Encounter?subject.name".
1070         *
1071         * @since 6.6.0
1072         */
1073        public boolean isIndexOnUpliftedRefchains() {
1074                return myIndexOnUpliftedRefchains;
1075        }
1076
1077        /**
1078         * If enabled, "Uplifted Refchains" will be enabled. This feature causes
1079         * HAPI FHIR to generate indexes for stored resources that include the current
1080         * value of the target of a chained reference, such as "Encounter?subject.name".
1081         *
1082         * @since 6.6.0
1083         */
1084        public void setIndexOnUpliftedRefchains(boolean theIndexOnUpliftedRefchains) {
1085                myIndexOnUpliftedRefchains = theIndexOnUpliftedRefchains;
1086        }
1087
1088        /**
1089         * Should indexing and searching on contained resources be enabled on this server.
1090         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1091         *
1092         * @since 5.4.0
1093         */
1094        public boolean isIndexOnContainedResources() {
1095                return myIndexOnContainedResources;
1096        }
1097
1098        /**
1099         * Should indexing and searching on contained resources be enabled on this server.
1100         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1101         *
1102         * @since 5.4.0
1103         */
1104        public void setIndexOnContainedResources(boolean theIndexOnContainedResources) {
1105                myIndexOnContainedResources = theIndexOnContainedResources;
1106        }
1107
1108        /**
1109         * Should recursive indexing and searching on contained resources be enabled on this server.
1110         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1111         *
1112         * @since 5.6.0
1113         */
1114        public boolean isIndexOnContainedResourcesRecursively() {
1115                return myIndexOnContainedResourcesRecursively;
1116        }
1117
1118        /**
1119         * Should recursive indexing and searching on contained resources be enabled on this server.
1120         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1121         *
1122         * @since 5.6.0
1123         */
1124        public void setIndexOnContainedResourcesRecursively(boolean theIndexOnContainedResourcesRecursively) {
1125                myIndexOnContainedResourcesRecursively = theIndexOnContainedResourcesRecursively;
1126        }
1127
1128        /**
1129         * If this is disabled by setting this to {@literal false} (default is {@literal true}),
1130         * the server will not automatically implement and support search parameters that
1131         * are not explcitly created in the repository.
1132         * <p>
1133         * Disabling this can have a dramatic improvement on performance (especially write performance)
1134         * in servers that only need to support a small number of search parameters, or no search parameters at all.
1135         * Disabling this obviously reduces the options for searching however.
1136         * </p>
1137         *
1138         * @since 5.7.0
1139         */
1140        public boolean isAutoSupportDefaultSearchParams() {
1141                return myAutoSupportDefaultSearchParams;
1142        }
1143
1144        /**
1145         * If this is disabled by setting this to {@literal false} (default is {@literal true}),
1146         * the server will not automatically implement and support search parameters that
1147         * are not explcitly created in the repository.
1148         * <p>
1149         * Disabling this can have a dramatic improvement on performance (especially write performance)
1150         * in servers that only need to support a small number of search parameters, or no search parameters at all.
1151         * Disabling this obviously reduces the options for searching however.
1152         * </p>
1153         *
1154         * @since 5.7.0
1155         */
1156        public void setAutoSupportDefaultSearchParams(boolean theAutoSupportDefaultSearchParams) {
1157                myAutoSupportDefaultSearchParams = theAutoSupportDefaultSearchParams;
1158        }
1159
1160        /**
1161         * @return Should the {@literal _lamguage} SearchParameter be supported on this server? Defaults to {@literal false}.
1162         * @since 7.0.0
1163         */
1164        public boolean isLanguageSearchParameterEnabled() {
1165                return myLanguageSearchParameterEnabled;
1166        }
1167
1168        /**
1169         * Should the {@literal _lamguage} SearchParameter be supported on this server? Defaults to {@literal false}.
1170         *
1171         * @since 7.0.0
1172         */
1173        public void setLanguageSearchParameterEnabled(boolean theLanguageSearchParameterEnabled) {
1174                myLanguageSearchParameterEnabled = theLanguageSearchParameterEnabled;
1175        }
1176
1177        /**
1178         * @return true if the filter is enabled for resources installed via package installer, false otherwise
1179         * @since 7.0.0
1180         */
1181        public boolean isValidateResourceStatusForPackageUpload() {
1182                return myValidateResourceStatusForPackageUpload;
1183        }
1184
1185        /**
1186         * Should resources being installed via package installer be filtered.
1187         * @since 7.0.0
1188         */
1189        public void setValidateResourceStatusForPackageUpload(boolean theValidateResourceStatusForPackageUpload) {
1190                myValidateResourceStatusForPackageUpload = theValidateResourceStatusForPackageUpload;
1191        }
1192
1193        private static void validateTreatBaseUrlsAsLocal(String theUrl) {
1194                Validate.notBlank(theUrl, "Base URL must not be null or empty");
1195
1196                int starIdx = theUrl.indexOf('*');
1197                if (starIdx != -1) {
1198                        if (starIdx != theUrl.length() - 1) {
1199                                throw new IllegalArgumentException(Msg.code(1525)
1200                                                + "Base URL wildcard character (*) can only appear at the end of the string: " + theUrl);
1201                        }
1202                }
1203        }
1204
1205        public enum IndexEnabledEnum {
1206                ENABLED,
1207                DISABLED
1208        }
1209}