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