001package ca.uhn.fhir.jpa.searchparam.nickname;
002
003/*-
004 * #%L
005 * HAPI FHIR Search Parameters
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.interceptor.api.Hook;
024import ca.uhn.fhir.interceptor.api.Pointcut;
025import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
026import ca.uhn.fhir.model.api.IQueryParameterType;
027import ca.uhn.fhir.rest.param.StringParam;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import java.io.IOException;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.List;
035import java.util.Locale;
036import java.util.Map;
037
038public class NicknameInterceptor {
039        private static final Logger ourLog = LoggerFactory.getLogger(NicknameInterceptor.class);
040
041        private final NicknameSvc myNicknameSvc;
042
043        public NicknameInterceptor() throws IOException {
044                myNicknameSvc = new NicknameSvc();
045        }
046
047        @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED)
048        public void expandNicknames(SearchParameterMap theSearchParameterMap) {
049                for (Map.Entry<String, List<List<IQueryParameterType>>> set : theSearchParameterMap.entrySet()) {
050                        String paramName = set.getKey();
051                        List<List<IQueryParameterType>> andList = set.getValue();
052                        for (List<IQueryParameterType> orList : andList) {
053                                // here we will know if it's an _id param or not
054                                // from theSearchParameterMap.keySet()
055                                expandAnyNicknameParameters(paramName, orList);
056                        }
057                }
058        }
059
060        /**
061         * If a Parameter is a string parameter, and it has been set to expand Nicknames, perform the expansion.
062         */
063        private void expandAnyNicknameParameters(String theParamName, List<IQueryParameterType> orList) {
064                List<IQueryParameterType> toAdd = new ArrayList<>();
065                List<IQueryParameterType> toRemove = new ArrayList<>();
066                for (IQueryParameterType iQueryParameterType : orList) {
067                        if (iQueryParameterType instanceof StringParam) {
068                                StringParam stringParam = (StringParam) iQueryParameterType;
069                                if (stringParam.isNicknameExpand()) {
070                                        ourLog.debug("Found a nickname parameter to expand: {} {}", theParamName, stringParam);
071                                        toRemove.add(stringParam);
072                                        //First, attempt to expand as a formal name
073                                        String name = stringParam.getValue().toLowerCase(Locale.ROOT);
074                                        Collection<String> expansions = myNicknameSvc.getEquivalentNames(name);
075                                        if (expansions == null) {
076                                                continue;
077                                        }
078                                        ourLog.debug("Parameter has been expanded to: {} {}", theParamName, String.join(", ", expansions));
079                                        expansions.stream()
080                                                .map(StringParam::new)
081                                                .forEach(toAdd::add);
082                                }
083                        }
084                }
085                orList.removeAll(toRemove);
086                orList.addAll(toAdd);
087        }
088}