001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 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.context;
021
022import ca.uhn.fhir.parser.IParser;
023import ca.uhn.fhir.util.CollectionUtil;
024import jakarta.annotation.Nonnull;
025import jakarta.annotation.Nullable;
026import org.apache.commons.lang3.Validate;
027
028import java.util.Collection;
029import java.util.Collections;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Set;
033import java.util.stream.Collectors;
034
035/**
036 * This object supplies default configuration to all {@link IParser parser} instances
037 * created by a given {@link FhirContext}. It is accessed using {@link FhirContext#getParserOptions()}
038 * and {@link FhirContext#setParserOptions(ParserOptions)}.
039 * <p>
040 * It is fine to share a ParserOptions instances across multiple context instances.
041 * </p>
042 */
043public class ParserOptions {
044
045        private boolean myStripVersionsFromReferences = true;
046        private Set<String> myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
047        private boolean myOverrideResourceIdWithBundleEntryFullUrl = true;
048        private boolean myAutoContainReferenceTargetsWithNoId = true;
049        private Set<String> myEncodeElementsForSummaryMode = null;
050        private Set<String> myDontEncodeElementsForSummaryMode = null;
051
052        /**
053         * If set to {@literal true} (which is the default), contained resources may be specified by
054         * populating the target (contained) resource directly in {@link org.hl7.fhir.instance.model.api.IBaseReference#setReference(String)}
055         * and the parser will automatically locate it and insert it into <code>Resource.contained</code> when
056         * serializing. This is convenient, but also imposes a performance cost when serializing large numbers
057         * of resources, so this can be disabled if it is not needed.
058         * <p>
059         * If disabled, only resources that are directly placed in <code>Resource.contained</code> will be
060         * serialized.
061         * </p>
062         *
063         * @since 5.7.0
064         */
065        public boolean isAutoContainReferenceTargetsWithNoId() {
066                return myAutoContainReferenceTargetsWithNoId;
067        }
068
069        /**
070         * If set to {@literal true} (which is the default), contained resources may be specified by
071         * populating the target (contained) resource directly in {@link org.hl7.fhir.instance.model.api.IBaseReference#setReference(String)}
072         * and the parser will automatically locate it and insert it into <code>Resource.contained</code> when
073         * serializing. This is convenient, but also imposes a performance cost when serializing large numbers
074         * of resources, so this can be disabled if it is not needed.
075         * <p>
076         * If disabled, only resources that are directly placed in <code>Resource.contained</code> will be
077         * serialized.
078         * </p>
079         *
080         * @since 5.7.0
081         */
082        public void setAutoContainReferenceTargetsWithNoId(boolean theAllowAutoContainedReferences) {
083                myAutoContainReferenceTargetsWithNoId = theAllowAutoContainedReferences;
084        }
085
086        /**
087         * If set to <code>true<code> (which is the default), resource references containing a version
088         * will have the version removed when the resource is encoded. This is generally good behaviour because
089         * in most situations, references from one resource to another should be to the resource by ID, not
090         * by ID and version. In some cases though, it may be desirable to preserve the version in resource
091         * links. In that case, this value should be set to <code>false</code>.
092         *
093         * @return Returns the parser instance's configuration setting for stripping versions from resource references when
094         * encoding. Default is <code>true</code>.
095         */
096        public boolean isStripVersionsFromReferences() {
097                return myStripVersionsFromReferences;
098        }
099
100        /**
101         * If set to <code>true<code> (which is the default), resource references containing a version
102         * will have the version removed when the resource is encoded. This is generally good behaviour because
103         * in most situations, references from one resource to another should be to the resource by ID, not
104         * by ID and version. In some cases though, it may be desirable to preserve the version in resource
105         * links. In that case, this value should be set to <code>false</code>.
106         * <p>
107         * This method provides the ability to globally disable reference encoding. If finer-grained
108         * control is needed, use {@link #setDontStripVersionsFromReferencesAtPaths(String...)}
109         * </p>
110         *
111         * @param theStripVersionsFromReferences Set this to <code>false<code> to prevent the parser from removing
112         *                                       resource versions from references.
113         * @return Returns a reference to <code>this</code> parser so that method calls can be chained together
114         * @see #setDontStripVersionsFromReferencesAtPaths(String...)
115         */
116        public ParserOptions setStripVersionsFromReferences(boolean theStripVersionsFromReferences) {
117                myStripVersionsFromReferences = theStripVersionsFromReferences;
118                return this;
119        }
120
121        /**
122         * Returns the value supplied to {@link IParser#setDontStripVersionsFromReferencesAtPaths(String...)}
123         *
124         * @see #setDontStripVersionsFromReferencesAtPaths(String...)
125         * @see #setStripVersionsFromReferences(boolean)
126         */
127        public Set<String> getDontStripVersionsFromReferencesAtPaths() {
128                return myDontStripVersionsFromReferencesAtPaths;
129        }
130
131        /**
132         * Returns a sub-collection of {@link #getDontStripVersionsFromReferencesAtPaths()} containing only paths
133         * for the given resource type.
134         */
135        public Set<String> getDontStripVersionsFromReferencesAtPathsByResourceType(String theResourceType) {
136                Validate.notEmpty(theResourceType, "theResourceType must not be null or empty");
137                return myDontStripVersionsFromReferencesAtPaths.stream()
138                                .filter(referencePath -> referencePath.startsWith(theResourceType + "."))
139                                .collect(Collectors.toSet());
140        }
141
142        /**
143         * If supplied value(s), any resource references at the specified paths will have their
144         * resource versions encoded instead of being automatically stripped during the encoding
145         * process. This setting has no effect on the parsing process.
146         * <p>
147         * This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(boolean)}
148         * and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(boolean)}
149         * has been set to <code>true</code> (which is the default)
150         * </p>
151         *
152         * @param thePaths A collection of paths for which the resource versions will not be removed automatically
153         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
154         *                 only resource name and field names with dots separating is allowed here (no repetition
155         *                 indicators, FluentPath expressions, etc.)
156         * @return Returns a reference to <code>this</code> parser so that method calls can be chained together
157         * @see #setStripVersionsFromReferences(boolean)
158         */
159        public ParserOptions setDontStripVersionsFromReferencesAtPaths(String... thePaths) {
160                if (thePaths == null) {
161                        setDontStripVersionsFromReferencesAtPaths((List<String>) null);
162                } else {
163                        setDontStripVersionsFromReferencesAtPaths(CollectionUtil.newSet(thePaths));
164                }
165                return this;
166        }
167
168        /**
169         * If supplied value(s), any resource references at the specified paths will have their
170         * resource versions encoded instead of being automatically stripped during the encoding
171         * process. This setting has no effect on the parsing process.
172         * <p>
173         * This method provides a finer-grained level of control than {@link #setStripVersionsFromReferences(boolean)}
174         * and any paths specified by this method will be encoded even if {@link #setStripVersionsFromReferences(boolean)}
175         * has been set to <code>true</code> (which is the default)
176         * </p>
177         *
178         * @param thePaths A collection of paths for which the resource versions will not be removed automatically
179         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
180         *                 only resource name and field names with dots separating is allowed here (no repetition
181         *                 indicators, FluentPath expressions, etc.)
182         * @return Returns a reference to <code>this</code> parser so that method calls can be chained together
183         * @see #setStripVersionsFromReferences(boolean)
184         */
185        @SuppressWarnings("unchecked")
186        public ParserOptions setDontStripVersionsFromReferencesAtPaths(Collection<String> thePaths) {
187                if (thePaths == null) {
188                        myDontStripVersionsFromReferencesAtPaths = Collections.emptySet();
189                } else if (thePaths instanceof HashSet) {
190                        myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>) thePaths).clone();
191                } else {
192                        myDontStripVersionsFromReferencesAtPaths = new HashSet<>(thePaths);
193                }
194                return this;
195        }
196
197        /**
198         * If set to <code>true</code> (which is the default), the Bundle.entry.fullUrl will override the Bundle.entry.resource's
199         * resource id if the fullUrl is defined. This behavior happens when parsing the source data into a Bundle object. Set this
200         * to <code>false</code> if this is not the desired behavior (e.g. the client code wishes to perform additional
201         * validation checks between the fullUrl and the resource id).
202         *
203         * @return Returns the parser instance's configuration setting for overriding resource ids with Bundle.entry.fullUrl when
204         * parsing the source data into a Bundle object. Default is <code>true</code>.
205         */
206        public boolean isOverrideResourceIdWithBundleEntryFullUrl() {
207                return myOverrideResourceIdWithBundleEntryFullUrl;
208        }
209
210        /**
211         * If set to <code>true</code> (which is the default), the Bundle.entry.fullUrl will override the Bundle.entry.resource's
212         * resource id if the fullUrl is defined. This behavior happens when parsing the source data into a Bundle object. Set this
213         * to <code>false</code> if this is not the desired behavior (e.g. the client code wishes to perform additional
214         * validation checks between the fullUrl and the resource id).
215         *
216         * @param theOverrideResourceIdWithBundleEntryFullUrl Set this to <code>false</code> to prevent the parser from overriding resource ids with the
217         *                                                    Bundle.entry.fullUrl
218         * @return Returns a reference to <code>this</code> parser so that method calls can be chained together
219         */
220        public ParserOptions setOverrideResourceIdWithBundleEntryFullUrl(
221                        boolean theOverrideResourceIdWithBundleEntryFullUrl) {
222                myOverrideResourceIdWithBundleEntryFullUrl = theOverrideResourceIdWithBundleEntryFullUrl;
223                return this;
224        }
225
226        /**
227         * This option specifies one or more elements that should be included when the parser is encoding
228         * a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is not
229         * a part of the base FHIR specification's list of summary elements. Examples of valid values
230         * include:
231         * <ul>
232         * <li><b>Patient.maritalStatus</b> - Encode the entire maritalStatus CodeableConcept, even though Patient.maritalStatus is not a summary element</li>
233         * <li><b>Patient.maritalStatus.text</b> - Encode only the text component of the patient's maritalStatus</li>
234         * <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
235         * wildcard)</li>
236         * </ul>
237         *
238         * @see IParser#setSummaryMode(boolean)
239         * @see IParser#setEncodeElements(Set) Can be used to specify these values for an individual parser instance.
240         * @since 7.4.0
241         */
242        @SuppressWarnings({"UnusedReturnValue"})
243        @Nonnull
244        public ParserOptions setEncodeElementsForSummaryMode(@Nonnull String... theEncodeElements) {
245                return setEncodeElementsForSummaryMode(CollectionUtil.newSet(theEncodeElements));
246        }
247
248        /**
249         * This option specifies one or more elements that should be included when the parser is encoding
250         * a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is not
251         * a part of the base FHIR specification's list of summary elements. Examples of valid values
252         * include:
253         * <ul>
254         * <li><b>Patient.maritalStatus</b> - Encode the entire maritalStatus CodeableConcept, even though Patient.maritalStatus is not a summary element</li>
255         * <li><b>Patient.maritalStatus.text</b> - Encode only the text component of the patient's maritalStatus</li>
256         * <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
257         * wildcard)</li>
258         * </ul>
259         *
260         * @see IParser#setSummaryMode(boolean)
261         * @see IParser#setEncodeElements(Set) Can be used to specify these values for an individual parser instance.
262         * @since 7.4.0
263         */
264        @Nonnull
265        public ParserOptions setEncodeElementsForSummaryMode(@Nullable Collection<String> theEncodeElements) {
266                Set<String> encodeElements = null;
267                if (theEncodeElements != null && !theEncodeElements.isEmpty()) {
268                        encodeElements = new HashSet<>(theEncodeElements);
269                }
270                myEncodeElementsForSummaryMode = encodeElements;
271                return this;
272        }
273
274        /**
275         * @return Returns the values provided to {@link #setEncodeElementsForSummaryMode(Collection)}
276         * or <code>null</code>
277         *
278         * @since 7.4.0
279         */
280        @Nullable
281        public Set<String> getEncodeElementsForSummaryMode() {
282                return myEncodeElementsForSummaryMode;
283        }
284
285        /**
286         * This option specifies one or more elements that should be excluded when the parser is encoding
287         * a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is
288         * a part of the base FHIR specification's list of summary elements. Examples of valid values
289         * include:
290         * <ul>
291         * <li><b>Patient.name</b> - Do not include the patient's name</li>
292         * <li><b>Patient.name.family</b> - Do not include the patient's family name</li>
293         * <li><b>*.name</b> - Do not include the name element on any resource type</li>
294         * </ul>
295         *
296         * @see IParser#setSummaryMode(boolean)
297         * @see IParser#setDontEncodeElements(Collection) Can be used to specify these values for an individual parser instance.
298         * @since 7.4.0
299         */
300        @SuppressWarnings({"UnusedReturnValue"})
301        @Nonnull
302        public ParserOptions setDontEncodeElementsForSummaryMode(@Nonnull String... theEncodeElements) {
303                return setDontEncodeElementsForSummaryMode(CollectionUtil.newSet(theEncodeElements));
304        }
305
306        /**
307         * This option specifies one or more elements that should be excluded when the parser is encoding
308         * a resource in {@link IParser#setSummaryMode(boolean) summary mode}, even if the element is
309         * a part of the base FHIR specification's list of summary elements. Examples of valid values
310         * include:
311         * <ul>
312         * <li><b>Patient.name</b> - Do not include the patient's name</li>
313         * <li><b>Patient.name.family</b> - Do not include the patient's family name</li>
314         * <li><b>*.name</b> - Do not include the name element on any resource type</li>
315         * </ul>
316         *
317         * @see IParser#setSummaryMode(boolean)
318         * @see IParser#setDontEncodeElements(Collection) Can be used to specify these values for an individual parser instance.
319         * @since 7.4.0
320         */
321        @Nonnull
322        public ParserOptions setDontEncodeElementsForSummaryMode(@Nullable Collection<String> theDontEncodeElements) {
323                Set<String> dontEncodeElements = null;
324                if (theDontEncodeElements != null && !theDontEncodeElements.isEmpty()) {
325                        dontEncodeElements = new HashSet<>(theDontEncodeElements);
326                }
327                myDontEncodeElementsForSummaryMode = dontEncodeElements;
328                return this;
329        }
330
331        /**
332         * @return Returns the values provided to {@link #setDontEncodeElementsForSummaryMode(Collection)}
333         * or <code>null</code>
334         * @since 7.4.0
335         */
336        @Nullable
337        public Set<String> getDontEncodeElementsForSummaryMode() {
338                return myDontEncodeElementsForSummaryMode;
339        }
340}