001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2025 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.api.model.json;
011
012import ca.uhn.fhir.model.api.IModelJson;
013import ca.uhn.fhir.model.api.annotation.ExampleSupplier;
014import com.fasterxml.jackson.annotation.JsonProperty;
015import io.swagger.v3.oas.annotations.media.Schema;
016import jakarta.annotation.Nonnull;
017import jakarta.annotation.Nullable;
018
019import java.util.Objects;
020import java.util.function.Supplier;
021
022import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
023
024@SuppressWarnings("UnusedReturnValue")
025@Schema(name = "TokenizationRule", description = "Defines a single rule for FHIR Repository Tokenization")
026@ExampleSupplier(TokenizationRuleJson.ExampleSupplier.class)
027public class TokenizationRuleJson implements IModelJson, Cloneable {
028
029        @Schema(
030                        description =
031                                        "An optional description of this rule. This field does not affect the functionality of the rule, but can be used as documentation.")
032        @JsonProperty(value = "description", required = false)
033        private String myDescription;
034
035        @Schema(
036                        description =
037                                        "A FHIRPath expression within a resource pointing to the element to tokenize. The value must begin with a resource type, e.g. `Patient.name.family`.")
038        @JsonProperty(value = "path", required = true)
039        private String myPath;
040
041        @JsonProperty(value = "searchParameter", required = false)
042        @Schema(
043                        description =
044                                        "The name of a SearchParameter which can be used to search for the tokenized value. If the FHIRPath expression in this rule matches the path of an existing SearchParameter, it is recommended to use the name of that SearchParameter here. This may be left empty, in which case it will not be possible to perform a search for the tokenized value (in other words, this should only be populated for rules containing paths which should be indexed for searching).")
045        private String mySearchParameter;
046
047        @JsonProperty(value = "searchValueNormalization", required = false)
048        @Schema(
049                        description =
050                                        "If this property is set, the element value will be tokenized twice. The first value contains the original element as it was stored in the resource, and the second contains a normalized version of the element. The normalized version is used for search indexing. See [Search Normalization](/docs/fhir_repository/tokenization.html#search-normalization) for more information.")
051        private TokenizationValueNormalizationEnum mySearchValueNormalization;
052
053        @JsonProperty(value = "neverDetokenize", required = false)
054        @Schema(
055                        description =
056                                        "If set to true, the system will never attempt to de-tokenize the value. This could be set if the configured tokenization algorithm is not reversible, or the usage scenario means that it is not desirable to reverse the tokenization on this element.")
057        private boolean myNeverDetokenize;
058
059        @JsonProperty(value = "status", required = false)
060        @Schema(
061                        description =
062                                        "The status of this rule. If not set, defaults to ACTIVE. Setting the status to QUIESCE means that the rule is still available for detokenization but new data should not be tokenized with this rule. See [Updating Tokenization Rules](/docs/fhir_repository/tokenization.html#update-tokenization-rules) for more information.")
063        private RuleStatusEnum myStatus;
064
065        /**
066         * Constructor
067         */
068        public TokenizationRuleJson() {
069                super();
070        }
071
072        /**
073         * Copy Constructor
074         */
075        public TokenizationRuleJson(TokenizationRuleJson theTokenizationRuleJson) {
076                myDescription = theTokenizationRuleJson.myDescription;
077                myPath = theTokenizationRuleJson.myPath;
078                mySearchParameter = theTokenizationRuleJson.mySearchParameter;
079                mySearchValueNormalization = theTokenizationRuleJson.mySearchValueNormalization;
080                myNeverDetokenize = theTokenizationRuleJson.myNeverDetokenize;
081                myStatus = theTokenizationRuleJson.myStatus;
082        }
083
084        /**
085         * Defaults to {@link RuleStatusEnum#ACTIVE} if not set
086         * @since 2025.11
087         */
088        @Nonnull
089        public RuleStatusEnum getStatus() {
090                return defaultIfNull(myStatus, RuleStatusEnum.ACTIVE);
091        }
092
093        public TokenizationRuleJson setStatus(RuleStatusEnum theStatus) {
094                myStatus = theStatus;
095                return this;
096        }
097
098        public String getDescription() {
099                return myDescription;
100        }
101
102        public TokenizationRuleJson setDescription(String theDescription) {
103                myDescription = theDescription;
104                return this;
105        }
106
107        public String getPath() {
108                return myPath;
109        }
110
111        public TokenizationRuleJson setPath(String thePath) {
112                myPath = thePath;
113                return this;
114        }
115
116        public String getSearchParameter() {
117                return mySearchParameter;
118        }
119
120        public TokenizationRuleJson setSearchParameter(String theSearchParameter) {
121                mySearchParameter = theSearchParameter;
122                return this;
123        }
124
125        @Nullable
126        public TokenizationValueNormalizationEnum getSearchValueNormalization() {
127                return mySearchValueNormalization;
128        }
129
130        public TokenizationRuleJson setSearchValueNormalization(
131                        TokenizationValueNormalizationEnum theSearchValueNormalization) {
132                mySearchValueNormalization = theSearchValueNormalization;
133                return this;
134        }
135
136        @Nullable
137        public String getResourceType() {
138                int dotIdx = myPath.indexOf('.');
139                if (dotIdx != -1) {
140                        return myPath.substring(0, dotIdx);
141                }
142                return null;
143        }
144
145        public boolean isNeverDetokenize() {
146                return myNeverDetokenize;
147        }
148
149        public TokenizationRuleJson setNeverDetokenize(boolean theNeverDetokenize) {
150                myNeverDetokenize = theNeverDetokenize;
151                return this;
152        }
153
154        @SuppressWarnings("MethodDoesntCallSuperMethod")
155        @Override
156        public TokenizationRuleJson clone() {
157                return new TokenizationRuleJson(this);
158        }
159
160        @Override
161        public boolean equals(Object theO) {
162                if (!(theO instanceof TokenizationRuleJson that)) {
163                        return false;
164                }
165                return myNeverDetokenize == that.myNeverDetokenize
166                                && Objects.equals(myDescription, that.myDescription)
167                                && Objects.equals(myPath, that.myPath)
168                                && Objects.equals(mySearchParameter, that.mySearchParameter)
169                                && mySearchValueNormalization == that.mySearchValueNormalization;
170        }
171
172        @Override
173        public int hashCode() {
174                return Objects.hash(myDescription, myPath, mySearchParameter, mySearchValueNormalization, myNeverDetokenize);
175        }
176
177        @Override
178        public String toString() {
179                return "TokenizationRuleJson[" + "path='"
180                                + myPath + '\'' + ", description='"
181                                + myDescription + '\'' + ", searchParameter='"
182                                + mySearchParameter + '\'' + ", searchValueNormalization="
183                                + mySearchValueNormalization + ", neverDetokenize="
184                                + myNeverDetokenize + ']';
185        }
186
187        public enum RuleStatusEnum {
188                /**
189                 * Rule is active - this is the default
190                 */
191                @Schema(description = "Rule is active - this is the default.")
192                ACTIVE,
193
194                /**
195                 * Rule is being disabled: existing data stored with this rule can be detokenized, but new data
196                 * should not be tokenized with this rule.
197                 */
198                @Schema(
199                                description =
200                                                "Rule is being disabled: existing data stored with this rule can be detokenized, but new data should not be tokenized with this rule.")
201                QUIESCE,
202
203                /**
204                 * Rule is completely ignored
205                 */
206                @Schema(description = "Rule is completely ignored.")
207                DISABLED
208        }
209
210        public static class ExampleSupplier implements Supplier<TokenizationRuleJson> {
211                @Override
212                public TokenizationRuleJson get() {
213                        TokenizationRuleJson rule = new TokenizationRuleJson();
214                        rule.setPath("Patient.name.family");
215                        rule.setDescription("Patient family name");
216                        rule.setSearchParameter("family");
217                        rule.setSearchValueNormalization(TokenizationValueNormalizationEnum.STRING);
218                        rule.setStatus(TokenizationRuleJson.RuleStatusEnum.ACTIVE);
219                        return rule;
220                }
221        }
222}