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.context.ConfigurationException; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 025import jakarta.annotation.Nonnull; 026 027import java.util.HashMap; 028import java.util.HashSet; 029import java.util.Map; 030import java.util.Set; 031import java.util.stream.Collectors; 032 033public class VectorMatchResultMap { 034 private final MdmRulesJson myMdmRulesJson; 035 private Map<Long, MdmMatchResultEnum> myVectorToMatchResultMap = new HashMap<>(); 036 private Set<Long> myMatchVectors = new HashSet<>(); 037 private Set<Long> myPossibleMatchVectors = new HashSet<>(); 038 private Map<Long, String> myVectorToFieldMatchNamesMap = new HashMap<>(); 039 040 VectorMatchResultMap(MdmRulesJson theMdmRulesJson) { 041 myMdmRulesJson = theMdmRulesJson; 042 // no reason to hold the entire mdmRulesJson here 043 initMap(); 044 } 045 046 private void initMap() { 047 for (Map.Entry<String, MdmMatchResultEnum> entry : 048 myMdmRulesJson.getMatchResultMap().entrySet()) { 049 put(entry.getKey(), entry.getValue()); 050 } 051 } 052 053 @Nonnull 054 public MdmMatchResultEnum get(Long theMatchVector) { 055 return myVectorToMatchResultMap.computeIfAbsent(theMatchVector, this::computeMatchResult); 056 } 057 058 private MdmMatchResultEnum computeMatchResult(Long theVector) { 059 if (myMatchVectors.stream().anyMatch(v -> (v & theVector) == v)) { 060 return MdmMatchResultEnum.MATCH; 061 } 062 if (myPossibleMatchVectors.stream().anyMatch(v -> (v & theVector) == v)) { 063 return MdmMatchResultEnum.POSSIBLE_MATCH; 064 } 065 return MdmMatchResultEnum.NO_MATCH; 066 } 067 068 public Set<String> getMatchedRules(Long theVector) { 069 if (theVector == null) { 070 return new HashSet<>(); 071 } 072 return myVectorToFieldMatchNamesMap.entrySet().stream() 073 .filter(e -> ((e.getKey() & theVector) == e.getKey())) 074 .map(Map.Entry::getValue) 075 .collect(Collectors.toSet()); 076 } 077 078 private void put(String theFieldMatchNames, MdmMatchResultEnum theMatchResult) { 079 long vector = getVector(theFieldMatchNames); 080 myVectorToFieldMatchNamesMap.put(vector, theFieldMatchNames); 081 myVectorToMatchResultMap.put(vector, theMatchResult); 082 if (theMatchResult == MdmMatchResultEnum.MATCH) { 083 myMatchVectors.add(vector); 084 } else if (theMatchResult == MdmMatchResultEnum.POSSIBLE_MATCH) { 085 myPossibleMatchVectors.add(vector); 086 } 087 } 088 089 public long getVector(String theFieldMatchNames) { 090 long retval = 0; 091 for (String fieldMatchName : splitFieldMatchNames(theFieldMatchNames)) { 092 int index = getFieldMatchIndex(fieldMatchName); 093 if (index == -1) { 094 throw new ConfigurationException(Msg.code(1523) + "There is no matchField with name " + fieldMatchName); 095 } 096 retval |= (1 << index); 097 } 098 return retval; 099 } 100 101 @Nonnull 102 static String[] splitFieldMatchNames(String theFieldMatchNames) { 103 return theFieldMatchNames.split(",\\s*"); 104 } 105 106 private int getFieldMatchIndex(final String theFieldMatchName) { 107 for (int i = 0; i < myMdmRulesJson.size(); ++i) { 108 if (myMdmRulesJson.get(i).getName().equals(theFieldMatchName)) { 109 return i; 110 } 111 } 112 return -1; 113 } 114 115 public String getFieldMatchNames(long theVector) { 116 return myVectorToFieldMatchNamesMap.get(theVector); 117 } 118 119 public Set<String> getAllFieldMatchNames() { 120 return myVectorToFieldMatchNamesMap.keySet().stream() 121 .map(key -> myVectorToFieldMatchNamesMap.get(key)) 122 .collect(Collectors.toSet()); 123 } 124}