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