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