All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.uhn.fhir.jpa.searchparam.SearchParameterMap Maven / Gradle / Ivy

/*
 * #%L
 * HAPI FHIR JPA - Search Parameters
 * %%
 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package ca.uhn.fhir.jpa.searchparam;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.util.UrlUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.annotation.Nonnull;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

public class SearchParameterMap implements Serializable {
	public static final Integer INTEGER_0 = 0;
	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMap.class);
	private static final long serialVersionUID = 1L;
	private final HashMap>> mySearchParameterMap = new LinkedHashMap<>();
	private Integer myCount;
	private Integer myOffset;
	private EverythingModeEnum myEverythingMode = null;
	private Set myIncludes;
	private DateRangeParam myLastUpdated;
	private boolean myLoadSynchronous;
	private Integer myLoadSynchronousUpTo;
	private Set myRevIncludes;
	private SortSpec mySort;
	private SummaryEnum mySummaryMode;
	private SearchTotalModeEnum mySearchTotalMode;
	private QuantityParam myNearDistanceParam;
	private boolean myLastN;
	private Integer myLastNMax;
	private boolean myDeleteExpunge;
	private SearchContainedModeEnum mySearchContainedMode = SearchContainedModeEnum.FALSE;

	/**
	 * Constructor
	 */
	public SearchParameterMap() {
		super();
	}

	/**
	 * Constructor
	 */
	public SearchParameterMap(String theName, IQueryParameterType theParam) {
		add(theName, theParam);
	}

	/**
	 * Creates and returns a copy of this map
	 */
	@JsonIgnore
	@Override
	public SearchParameterMap clone() {
		SearchParameterMap map = new SearchParameterMap();
		map.setSummaryMode(getSummaryMode());
		map.setSort(getSort());
		map.setSearchTotalMode(getSearchTotalMode());
		map.setRevIncludes(getRevIncludes());
		map.setIncludes(getIncludes());
		map.setEverythingMode(getEverythingMode());
		map.setCount(getCount());
		map.setDeleteExpunge(isDeleteExpunge());
		map.setLastN(isLastN());
		map.setLastNMax(getLastNMax());
		map.setLastUpdated(getLastUpdated());
		map.setLoadSynchronous(isLoadSynchronous());
		map.setNearDistanceParam(getNearDistanceParam());
		map.setLoadSynchronousUpTo(getLoadSynchronousUpTo());
		map.setOffset(getOffset());
		map.setSearchContainedMode(getSearchContainedMode());

		for (Map.Entry>> entry : mySearchParameterMap.entrySet()) {
			List> andParams = entry.getValue();
			List> newAndParams = new ArrayList<>();
			for (List orParams : andParams) {
				List newOrParams = new ArrayList<>(orParams);
				newAndParams.add(newOrParams);
			}
			map.put(entry.getKey(), newAndParams);
		}

		return map;
	}

	public SummaryEnum getSummaryMode() {
		return mySummaryMode;
	}

	public void setSummaryMode(SummaryEnum theSummaryMode) {
		mySummaryMode = theSummaryMode;
	}

	public SearchTotalModeEnum getSearchTotalMode() {
		return mySearchTotalMode;
	}

	public void setSearchTotalMode(SearchTotalModeEnum theSearchTotalMode) {
		mySearchTotalMode = theSearchTotalMode;
	}

	public SearchParameterMap add(String theName, DateParam theDateParam) {
		add(theName, (IQueryParameterOr) theDateParam);
		return this;
	}

	@SuppressWarnings("unchecked")
	public SearchParameterMap add(String theName, IQueryParameterAnd theAnd) {
		if (theAnd == null) {
			return this;
		}
		if (!containsKey(theName)) {
			put(theName, new ArrayList<>());
		}

		List> paramList = get(theName);
		for (IQueryParameterOr next : theAnd.getValuesAsQueryTokens()) {
			if (next == null) {
				continue;
			}
			paramList.add((List) next.getValuesAsQueryTokens());
		}

		return this;
	}

	public SearchParameterMap add(String theName, IQueryParameterOr theOr) {
		if (theOr == null) {
			return this;
		}
		if (!containsKey(theName)) {
			put(theName, new ArrayList<>());
		}

		get(theName).add((List) theOr.getValuesAsQueryTokens());
		return this;
	}

	public Collection>> values() {
		return mySearchParameterMap.values();
	}

	public SearchParameterMap add(String theName, IQueryParameterType theParam) {
		assert !Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map

		if (theParam == null) {
			return this;
		}
		if (!containsKey(theName)) {
			put(theName, new ArrayList<>());
		}
		ArrayList list = new ArrayList<>();
		list.add(theParam);
		get(theName).add(list);

		return this;
	}

	public SearchParameterMap addInclude(Include theInclude) {
		getIncludes().add(theInclude);
		return this;
	}

	private void addLastUpdateParam(StringBuilder theBuilder, ParamPrefixEnum thePrefix, DateParam theDateParam) {
		if (theDateParam != null && isNotBlank(theDateParam.getValueAsString())) {
			addUrlParamSeparator(theBuilder);
			theBuilder.append(Constants.PARAM_LASTUPDATED);
			theBuilder.append('=');
			theBuilder.append(thePrefix.getValue());
			theBuilder.append(theDateParam.getValueAsString());
		}
	}

	public SearchParameterMap addRevInclude(Include theInclude) {
		getRevIncludes().add(theInclude);
		return this;
	}

	private void addUrlIncludeParams(StringBuilder b, String paramName, Set theList) {
		ArrayList list = new ArrayList<>(theList);

		list.sort(new IncludeComparator());
		for (Include nextInclude : list) {
			addUrlParamSeparator(b);
			b.append(paramName);
			if (nextInclude.isRecurse()) {
				b.append(Constants.PARAM_INCLUDE_QUALIFIER_RECURSE);
			}
			b.append('=');
			if (Constants.INCLUDE_STAR.equals(nextInclude.getValue())) {
				b.append(Constants.INCLUDE_STAR);
			} else {
				b.append(UrlUtil.escapeUrlParam(nextInclude.getParamType()));
				b.append(':');
				b.append(UrlUtil.escapeUrlParam(nextInclude.getParamName()));
				if (isNotBlank(nextInclude.getParamTargetType())) {
					b.append(':');
					b.append(nextInclude.getParamTargetType());
				}
			}
		}
	}

	private void addUrlParamSeparator(StringBuilder theB) {
		if (theB.length() == 0) {
			theB.append('?');
		} else {
			theB.append('&');
		}
	}

	public Integer getCount() {
		return myCount;
	}

	public SearchParameterMap setCount(Integer theCount) {
		myCount = theCount;
		return this;
	}

	public Integer getOffset() {
		return myOffset;
	}

	public void setOffset(Integer theOffset) {
		myOffset = theOffset;
	}

	public EverythingModeEnum getEverythingMode() {
		return myEverythingMode;
	}

	public void setEverythingMode(EverythingModeEnum theConsolidateMatches) {
		myEverythingMode = theConsolidateMatches;
	}

	public Set getIncludes() {
		if (myIncludes == null) {
			myIncludes = new HashSet<>();
		}
		return myIncludes;
	}

	public void setIncludes(Set theIncludes) {
		myIncludes = theIncludes;
	}

	/**
	 * Returns null if there is no last updated value
	 */
	public DateRangeParam getLastUpdated() {
		if (myLastUpdated != null) {
			if (myLastUpdated.isEmpty()) {
				myLastUpdated = null;
			}
		}
		return myLastUpdated;
	}

	public void setLastUpdated(DateRangeParam theLastUpdated) {
		myLastUpdated = theLastUpdated;
	}

	/**
	 * If set, tells the server to load these results synchronously, and not to load
	 * more than X results
	 */
	public Integer getLoadSynchronousUpTo() {
		return myLoadSynchronousUpTo;
	}

	/**
	 * If set, tells the server to load these results synchronously, and not to load
	 * more than X results. Note that setting this to a value will also set
	 * {@link #setLoadSynchronous(boolean)} to true
	 */
	public SearchParameterMap setLoadSynchronousUpTo(Integer theLoadSynchronousUpTo) {
		myLoadSynchronousUpTo = theLoadSynchronousUpTo;
		if (myLoadSynchronousUpTo != null) {
			setLoadSynchronous(true);
		}
		return this;
	}

	public Set getRevIncludes() {
		if (myRevIncludes == null) {
			myRevIncludes = new HashSet<>();
		}
		return myRevIncludes;
	}

	public void setRevIncludes(Set theRevIncludes) {
		myRevIncludes = theRevIncludes;
	}

	public SortSpec getSort() {
		return mySort;
	}

	public SearchParameterMap setSort(SortSpec theSort) {
		mySort = theSort;
		return this;
	}

	/**
	 * If set, tells the server to load these results synchronously, and not to load
	 * more than X results
	 */
	public boolean isLoadSynchronous() {
		return myLoadSynchronous;
	}

	/**
	 * If set, tells the server to load these results synchronously, and not to load
	 * more than X results
	 */
	public SearchParameterMap setLoadSynchronous(boolean theLoadSynchronous) {
		myLoadSynchronous = theLoadSynchronous;
		return this;
	}

	/**
	 * If set, tells the server to use an Elasticsearch query to generate a list of
	 * Resource IDs for the LastN operation
	 */
	public boolean isLastN() {
		return myLastN;
	}

	/**
	 * If set, tells the server to use an Elasticsearch query to generate a list of
	 * Resource IDs for the LastN operation
	 */
	public SearchParameterMap setLastN(boolean theLastN) {
		myLastN = theLastN;
		return this;
	}

	/**
	 * If set, tells the server the maximum number of observations to return for each
	 * observation code in the result set of a lastn operation
	 */
	public Integer getLastNMax() {
		return myLastNMax;
	}

	/**
	 * If set, tells the server the maximum number of observations to return for each
	 * observation code in the result set of a lastn operation
	 */
	public SearchParameterMap setLastNMax(Integer theLastNMax) {
		myLastNMax = theLastNMax;
		return this;
	}

	/**
	 * This method creates a URL query string representation of the parameters in this
	 * object, excluding the part before the parameters, e.g.
	 * 

* ?name=smith&_sort=Patient:family *

*

* This method excludes the _count parameter, * as it doesn't affect the substance of the results returned *

*/ public String toNormalizedQueryString(FhirContext theCtx) { StringBuilder b = new StringBuilder(); ArrayList keys = new ArrayList<>(keySet()); Collections.sort(keys); for (String nextKey : keys) { List> nextValuesAndsIn = get(nextKey); List> nextValuesAndsOut = new ArrayList<>(); for (List nextValuesAndIn : nextValuesAndsIn) { List nextValuesOrsOut = new ArrayList<>(); nextValuesOrsOut.addAll(nextValuesAndIn); nextValuesOrsOut.sort(new QueryParameterTypeComparator(theCtx)); if (nextValuesOrsOut.size() > 0) { nextValuesAndsOut.add(nextValuesOrsOut); } } // for AND nextValuesAndsOut.sort(new QueryParameterOrComparator(theCtx)); for (List nextValuesAnd : nextValuesAndsOut) { addUrlParamSeparator(b); IQueryParameterType firstValue = nextValuesAnd.get(0); b.append(UrlUtil.escapeUrlParam(nextKey)); if (firstValue.getMissing() != null) { b.append(Constants.PARAMQUALIFIER_MISSING); b.append('='); if (firstValue.getMissing()) { b.append(Constants.PARAMQUALIFIER_MISSING_TRUE); } else { b.append(Constants.PARAMQUALIFIER_MISSING_FALSE); } continue; } if (isNotBlank(firstValue.getQueryParameterQualifier())) { b.append(firstValue.getQueryParameterQualifier()); } b.append('='); for (int i = 0; i < nextValuesAnd.size(); i++) { IQueryParameterType nextValueOr = nextValuesAnd.get(i); if (i > 0) { b.append(','); } String valueAsQueryToken = nextValueOr.getValueAsQueryToken(theCtx); valueAsQueryToken = defaultString(valueAsQueryToken); b.append(UrlUtil.escapeUrlParam(valueAsQueryToken)); } } } // for keys SortSpec sort = getSort(); boolean first = true; while (sort != null) { if (isNotBlank(sort.getParamName())) { if (first) { addUrlParamSeparator(b); b.append(Constants.PARAM_SORT); b.append('='); first = false; } else { b.append(','); } if (sort.getOrder() == SortOrderEnum.DESC) { b.append('-'); } b.append(sort.getParamName()); } Validate.isTrue(sort != sort.getChain()); // just in case, shouldn't happen sort = sort.getChain(); } if (hasIncludes()) { addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes()); } addUrlIncludeParams(b, Constants.PARAM_REVINCLUDE, getRevIncludes()); if (getLastUpdated() != null) { DateParam lb = getLastUpdated().getLowerBound(); DateParam ub = getLastUpdated().getUpperBound(); if (isNotEqualsComparator(lb, ub)) { addLastUpdateParam(b, NOT_EQUAL, getLastUpdated().getLowerBound()); } else { addLastUpdateParam(b, GREATERTHAN_OR_EQUALS, lb); addLastUpdateParam(b, LESSTHAN_OR_EQUALS, ub); } } if (getCount() != null) { addUrlParamSeparator(b); b.append(Constants.PARAM_COUNT); b.append('='); b.append(getCount()); } if (getOffset() != null) { addUrlParamSeparator(b); b.append(Constants.PARAM_OFFSET); b.append('='); b.append(getOffset()); } // Summary mode (_summary) if (getSummaryMode() != null) { addUrlParamSeparator(b); b.append(Constants.PARAM_SUMMARY); b.append('='); b.append(getSummaryMode().getCode()); } // Search count mode (_total) if (getSearchTotalMode() != null) { addUrlParamSeparator(b); b.append(Constants.PARAM_SEARCH_TOTAL_MODE); b.append('='); b.append(getSearchTotalMode().getCode()); } // Contained mode // For some reason, instead of null here, we default to false. That said, ommitting it is identical to setting // it to false. if (getSearchContainedMode() != SearchContainedModeEnum.FALSE) { addUrlParamSeparator(b); b.append(Constants.PARAM_CONTAINED); b.append("="); b.append(getSearchContainedMode().getCode()); } if (b.length() == 0) { b.append('?'); } return b.toString(); } private boolean isNotEqualsComparator(DateParam theLowerBound, DateParam theUpperBound) { return theLowerBound != null && theUpperBound != null && theLowerBound.getPrefix().equals(NOT_EQUAL) && theUpperBound.getPrefix().equals(NOT_EQUAL); } /** * @since 5.5.0 */ public boolean hasIncludes() { return myIncludes != null && !myIncludes.isEmpty(); } /** * @since 6.2.0 */ public boolean hasRevIncludes() { return myRevIncludes != null && !myRevIncludes.isEmpty(); } @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); if (!isEmpty()) { b.append("params", mySearchParameterMap); } if (!getIncludes().isEmpty()) { b.append("includes", getIncludes()); } return b.toString(); } public void clean() { for (Map.Entry>> nextParamEntry : this.entrySet()) { String nextParamName = nextParamEntry.getKey(); List> andOrParams = nextParamEntry.getValue(); cleanParameter(nextParamName, andOrParams); } } /* * Given a particular named parameter, e.g. `name`, iterate over AndOrParams and remove any which are empty. */ private void cleanParameter(String theParamName, List> theAndOrParams) { theAndOrParams.forEach(orList -> { List emptyParameters = orList.stream() .filter(nextOr -> nextOr.getMissing() == null) .filter(nextOr -> nextOr instanceof QuantityParam) .filter(nextOr -> isBlank(((QuantityParam) nextOr).getValueAsString())) .collect(Collectors.toList()); ourLog.debug("Ignoring empty parameter: {}", theParamName); orList.removeAll(emptyParameters); }); theAndOrParams.removeIf(List::isEmpty); } public QuantityParam getNearDistanceParam() { return myNearDistanceParam; } public void setNearDistanceParam(QuantityParam theQuantityParam) { myNearDistanceParam = theQuantityParam; } public boolean isWantOnlyCount() { return SummaryEnum.COUNT.equals(getSummaryMode()) || INTEGER_0.equals(getCount()); } public boolean isDeleteExpunge() { return myDeleteExpunge; } public SearchParameterMap setDeleteExpunge(boolean theDeleteExpunge) { myDeleteExpunge = theDeleteExpunge; return this; } public List> get(String theName) { return mySearchParameterMap.get(theName); } public void put(String theName, List> theParams) { mySearchParameterMap.put(theName, theParams); } public boolean containsKey(String theName) { return mySearchParameterMap.containsKey(theName); } public Set keySet() { return mySearchParameterMap.keySet(); } public boolean isEmpty() { return mySearchParameterMap.isEmpty(); } // Wrapper methods public Set>>> entrySet() { return mySearchParameterMap.entrySet(); } public List> remove(String theName) { return mySearchParameterMap.remove(theName); } /** * Variant of removeByNameAndModifier for unmodified params. * * @param theName the query parameter key * @return an And/Or List of Query Parameters matching the name with no modifier. */ public List> removeByNameUnmodified(String theName) { return this.removeByNameAndModifier(theName, ""); } /** * Given a search parameter name and modifier (e.g. :text), * get and remove all Search Parameters matching this name and modifier * * @param theName the query parameter key * @param theModifier the qualifier you want to remove - nullable for unmodified params. * @return an And/Or List of Query Parameters matching the qualifier. */ public List> removeByNameAndModifier(String theName, String theModifier) { theModifier = StringUtils.defaultString(theModifier, ""); List> remainderParameters = new ArrayList<>(); List> matchingParameters = new ArrayList<>(); // pull all of them out, partition by match against the qualifier List> andList = mySearchParameterMap.remove(theName); if (andList != null) { for (List orList : andList) { if (!orList.isEmpty() && StringUtils.defaultString(orList.get(0).getQueryParameterQualifier(), "") .equals(theModifier)) { matchingParameters.add(orList); } else { remainderParameters.add(orList); } } } // put the unmatched back in. if (!remainderParameters.isEmpty()) { mySearchParameterMap.put(theName, remainderParameters); } return matchingParameters; } public List> removeByNameAndModifier( String theName, @Nonnull TokenParamModifier theModifier) { return removeByNameAndModifier(theName, theModifier.getValue()); } /** * For each search parameter in the map, extract any which have the given qualifier. * e.g. Take the url: Observation?code:text=abc&code=123&code:text=def&reason:text=somereason *

* If we call this function with `:text`, it will return a map that looks like: *

* code -> [[code:text=abc], [code:text=def]] * reason -> [[reason:text=somereason]] *

* and the remaining search parameters in the map will be: *

* code -> [[code=123]] * * @param theQualifier * @return */ public Map>> removeByQualifier(String theQualifier) { Map>> retVal = new HashMap<>(); Set parameterNames = mySearchParameterMap.keySet(); for (String parameterName : parameterNames) { List> paramsWithQualifier = removeByNameAndModifier(parameterName, theQualifier); retVal.put(parameterName, paramsWithQualifier); } return retVal; } public Map>> removeByQualifier(@Nonnull TokenParamModifier theModifier) { return removeByQualifier(theModifier.getValue()); } public int size() { return mySearchParameterMap.size(); } public SearchContainedModeEnum getSearchContainedMode() { return mySearchContainedMode; } public void setSearchContainedMode(SearchContainedModeEnum theSearchContainedMode) { if (theSearchContainedMode == null) { mySearchContainedMode = SearchContainedModeEnum.FALSE; } else { this.mySearchContainedMode = theSearchContainedMode; } } /** * Returns true if {@link #getOffset()} and {@link #getCount()} both return a non null response * * @since 5.5.0 */ public boolean isOffsetQuery() { return getOffset() != null && getCount() != null; } public enum EverythingModeEnum { /* * Don't reorder! We rely on the ordinals */ ENCOUNTER_INSTANCE(false, true, true), ENCOUNTER_TYPE(false, true, false), PATIENT_INSTANCE(true, false, true), PATIENT_TYPE(true, false, false); private final boolean myEncounter; private final boolean myInstance; private final boolean myPatient; EverythingModeEnum(boolean thePatient, boolean theEncounter, boolean theInstance) { assert thePatient ^ theEncounter; myPatient = thePatient; myEncounter = theEncounter; myInstance = theInstance; } public boolean isEncounter() { return myEncounter; } public boolean isInstance() { return myInstance; } public boolean isPatient() { return myPatient; } } static int compare(FhirContext theCtx, IQueryParameterType theO1, IQueryParameterType theO2) { CompareToBuilder b = new CompareToBuilder(); b.append(theO1.getMissing(), theO2.getMissing()); b.append(theO1.getQueryParameterQualifier(), theO2.getQueryParameterQualifier()); if (b.toComparison() == 0) { b.append(theO1.getValueAsQueryToken(theCtx), theO2.getValueAsQueryToken(theCtx)); } return b.toComparison(); } public static SearchParameterMap newSynchronous() { SearchParameterMap retVal = new SearchParameterMap(); retVal.setLoadSynchronous(true); return retVal; } public static SearchParameterMap newSynchronous(String theName, IQueryParameterType theParam) { SearchParameterMap retVal = new SearchParameterMap(); retVal.setLoadSynchronous(true); retVal.add(theName, theParam); return retVal; } public static class IncludeComparator implements Comparator { @Override public int compare(Include theO1, Include theO2) { int retVal = StringUtils.compare(theO1.getParamType(), theO2.getParamType()); if (retVal == 0) { retVal = StringUtils.compare(theO1.getParamName(), theO2.getParamName()); } if (retVal == 0) { retVal = StringUtils.compare(theO1.getParamTargetType(), theO2.getParamTargetType()); } return retVal; } } public static class QueryParameterOrComparator implements Comparator> { private final FhirContext myCtx; QueryParameterOrComparator(FhirContext theCtx) { myCtx = theCtx; } @Override public int compare(List theO1, List theO2) { // These lists will never be empty return SearchParameterMap.compare(myCtx, theO1.get(0), theO2.get(0)); } } public static class QueryParameterTypeComparator implements Comparator { private final FhirContext myCtx; QueryParameterTypeComparator(FhirContext theCtx) { myCtx = theCtx; } @Override public int compare(IQueryParameterType theO1, IQueryParameterType theO2) { return SearchParameterMap.compare(myCtx, theO1, theO2); } } public List getAllChainsInOrder() { final List allChainsInOrder = new ArrayList<>(); for (SortSpec sortSpec = getSort(); sortSpec != null; sortSpec = sortSpec.getChain()) { allChainsInOrder.add(sortSpec); } return Collections.unmodifiableList(allChainsInOrder); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy