001/*-
002 * #%L
003 * HAPI FHIR - Master Data Management
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.mdm.rules.json;
021
022import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
023import ca.uhn.fhir.model.api.IModelJson;
024import com.fasterxml.jackson.annotation.JsonProperty;
025import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
026import com.fasterxml.jackson.databind.util.StdConverter;
027import com.google.common.annotations.VisibleForTesting;
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.lang3.Validate;
030
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036
037import static ca.uhn.fhir.mdm.api.MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
038
039@JsonDeserialize(converter = MdmRulesJson.MdmRulesJsonConverter.class)
040public class MdmRulesJson implements IModelJson {
041
042        @JsonProperty(value = "version", required = true)
043        String myVersion;
044
045        @JsonProperty(value = "candidateSearchParams", required = true)
046        List<MdmResourceSearchParamJson> myCandidateSearchParams = new ArrayList<>();
047
048        @JsonProperty(value = "candidateFilterSearchParams", required = true)
049        List<MdmFilterSearchParamJson> myCandidateFilterSearchParams = new ArrayList<>();
050
051        @JsonProperty(value = "matchFields", required = true)
052        List<MdmFieldMatchJson> myMatchFieldJsonList = new ArrayList<>();
053
054        @JsonProperty(value = "matchResultMap", required = true)
055        Map<String, MdmMatchResultEnum> myMatchResultMap = new HashMap<>();
056
057        /**
058         * This field is deprecated, use eidSystems instead.
059         */
060        @Deprecated
061        @JsonProperty(value = "eidSystem")
062        String myEnterpriseEIDSystem;
063
064        @JsonProperty(value = "eidSystems")
065        Map<String, String> myEnterpriseEidSystems = new HashMap<>();
066
067        @JsonProperty(value = "mdmTypes")
068        List<String> myMdmTypes;
069
070        transient VectorMatchResultMap myVectorMatchResultMap;
071
072        public void addMatchField(MdmFieldMatchJson theMatchRuleName) {
073                myMatchFieldJsonList.add(theMatchRuleName);
074        }
075
076        public void addResourceSearchParam(MdmResourceSearchParamJson theSearchParam) {
077                myCandidateSearchParams.add(theSearchParam);
078        }
079
080        public void addFilterSearchParam(MdmFilterSearchParamJson theSearchParam) {
081                myCandidateFilterSearchParams.add(theSearchParam);
082        }
083
084        int size() {
085                return myMatchFieldJsonList.size();
086        }
087
088        MdmFieldMatchJson get(int theIndex) {
089                return myMatchFieldJsonList.get(theIndex);
090        }
091
092        MdmMatchResultEnum getMatchResult(String theFieldMatchNames) {
093                return myMatchResultMap.get(theFieldMatchNames);
094        }
095
096        public MdmMatchResultEnum getMatchResult(Long theMatchVector) {
097                return myVectorMatchResultMap.get(theMatchVector);
098        }
099
100        public void putMatchResult(String theFieldMatchNames, MdmMatchResultEnum theMatchResult) {
101                myMatchResultMap.put(theFieldMatchNames, theMatchResult);
102                initialize();
103        }
104
105        Map<String, MdmMatchResultEnum> getMatchResultMap() {
106                return Collections.unmodifiableMap(myMatchResultMap);
107        }
108
109        /**
110         * Must call initialize() before calling getMatchResult(Long)
111         */
112        public void initialize() {
113                myVectorMatchResultMap = new VectorMatchResultMap(this);
114        }
115
116        public List<MdmFieldMatchJson> getMatchFields() {
117                return Collections.unmodifiableList(myMatchFieldJsonList);
118        }
119
120        public List<MdmResourceSearchParamJson> getCandidateSearchParams() {
121                return Collections.unmodifiableList(myCandidateSearchParams);
122        }
123
124        public List<MdmFilterSearchParamJson> getCandidateFilterSearchParams() {
125                return Collections.unmodifiableList(myCandidateFilterSearchParams);
126        }
127
128        /**
129         * Use {@link this#getEnterpriseEIDSystemForResourceType(String)} instead.
130         */
131        @Deprecated
132        public String getEnterpriseEIDSystem() {
133                return myEnterpriseEIDSystem;
134        }
135
136        /**
137         * Use {@link this#setEnterpriseEIDSystems(Map)} (String)} or {@link this#addEnterpriseEIDSystem(String, String)} instead.
138         */
139        @Deprecated
140        public void setEnterpriseEIDSystem(String theEnterpriseEIDSystem) {
141                myEnterpriseEIDSystem = theEnterpriseEIDSystem;
142        }
143
144        public void setEnterpriseEIDSystems(Map<String, String> theEnterpriseEIDSystems) {
145                myEnterpriseEidSystems = theEnterpriseEIDSystems;
146        }
147
148        public void addEnterpriseEIDSystem(String theResourceType, String theEidSystem) {
149                if (myEnterpriseEidSystems == null) {
150                        myEnterpriseEidSystems = new HashMap<>();
151                }
152                myEnterpriseEidSystems.put(theResourceType, theEidSystem);
153        }
154
155        public Map<String, String> getEnterpriseEIDSystems() {
156                // First try the new property.
157                if (myEnterpriseEidSystems != null && !myEnterpriseEidSystems.isEmpty()) {
158                        return myEnterpriseEidSystems;
159                        // If that fails, fall back to our deprecated property.
160                } else if (!StringUtils.isBlank(myEnterpriseEIDSystem)) {
161                        HashMap<String, String> retVal = new HashMap<>();
162                        retVal.put(ALL_RESOURCE_SEARCH_PARAM_TYPE, myEnterpriseEIDSystem);
163                        return retVal;
164                        // Otherwise, return an empty map.
165                } else {
166                        return Collections.emptyMap();
167                }
168        }
169
170        public String getEnterpriseEIDSystemForResourceType(String theResourceType) {
171                Map<String, String> enterpriseEIDSystems = getEnterpriseEIDSystems();
172                if (enterpriseEIDSystems.containsKey(ALL_RESOURCE_SEARCH_PARAM_TYPE)) {
173                        return enterpriseEIDSystems.get(ALL_RESOURCE_SEARCH_PARAM_TYPE);
174                } else {
175                        return enterpriseEIDSystems.get(theResourceType);
176                }
177        }
178
179        public String getVersion() {
180                return myVersion;
181        }
182
183        public MdmRulesJson setVersion(String theVersion) {
184                myVersion = theVersion;
185                return this;
186        }
187
188        private void validate() {
189                Validate.notBlank(myVersion, "version may not be blank");
190
191                Map<String, String> enterpriseEIDSystems = getEnterpriseEIDSystems();
192
193                // If we have a * eid system, there should only be one.
194                if (enterpriseEIDSystems.containsKey(ALL_RESOURCE_SEARCH_PARAM_TYPE)) {
195                        Validate.isTrue(enterpriseEIDSystems.size() == 1);
196                }
197        }
198
199        public String getSummary() {
200                return myCandidateSearchParams.size() + " Candidate Search Params, " + myCandidateFilterSearchParams.size()
201                                + " Filter Search Params, " + myMatchFieldJsonList.size()
202                                + " Match Fields, " + myMatchResultMap.size()
203                                + " Match Result Entries";
204        }
205
206        public String getFieldMatchNamesForVector(long theVector) {
207                return myVectorMatchResultMap.getFieldMatchNames(theVector);
208        }
209
210        public String getDetailedFieldMatchResultWithSuccessInformation(long theVector) {
211                List<String> fieldMatchResult = new ArrayList<>();
212                for (int i = 0; i < myMatchFieldJsonList.size(); ++i) {
213                        if ((theVector & (1 << i)) == 0) {
214                                fieldMatchResult.add(myMatchFieldJsonList.get(i).getName() + ": NO");
215                        } else {
216                                fieldMatchResult.add(myMatchFieldJsonList.get(i).getName() + ": YES");
217                        }
218                }
219                return String.join("\n", fieldMatchResult);
220        }
221
222        @VisibleForTesting
223        VectorMatchResultMap getVectorMatchResultMapForUnitTest() {
224                return myVectorMatchResultMap;
225        }
226
227        /**
228         * Ensure the vector map is initialized after we deserialize
229         */
230        static class MdmRulesJsonConverter extends StdConverter<MdmRulesJson, MdmRulesJson> {
231
232                /**
233                 * This empty constructor is required by Jackson
234                 */
235                public MdmRulesJsonConverter() {}
236
237                @Override
238                public MdmRulesJson convert(MdmRulesJson theMdmRulesJson) {
239                        theMdmRulesJson.validate();
240                        theMdmRulesJson.initialize();
241                        return theMdmRulesJson;
242                }
243        }
244
245        public List<String> getMdmTypes() {
246                return myMdmTypes;
247        }
248
249        public void setMdmTypes(List<String> theMdmTypes) {
250                myMdmTypes = theMdmTypes;
251        }
252}