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}