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

ca.uhn.fhir.util.ParametersUtil Maven / Gradle / Ivy

There is a newer version: 7.4.0
Show newest version
/*
 * #%L
 * HAPI FHIR - Core Library
 * %%
 * 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.util;

import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.StringDt;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * Utilities for dealing with parameters resources in a version indepenedent way
 */
public class ParametersUtil {

	public static Optional getNamedParameterValueAsString(
			FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
		Function, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
		return extractNamedParameters(theCtx, theParameters, theParameterName, mapper).stream()
				.findFirst();
	}

	public static List getNamedParameterValuesAsString(
			FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
		Function, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
		return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
	}

	public static List getNamedParameterValuesAsInteger(
			FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
		Function, Integer> mapper = t -> (Integer) t.getValue();
		return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
	}

	public static Optional getNamedParameterValueAsInteger(
			FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
		return getNamedParameterValuesAsInteger(theCtx, theParameters, theParameterName).stream()
				.findFirst();
	}

	public static Optional getNamedParameter(
			FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
		return getNamedParameters(theCtx, theParameters, theParameterName).stream()
				.findFirst();
	}

	public static List getNamedParameters(
			FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
		Validate.notNull(theParameters, "theParameters must not be null");
		RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
		BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
		List parameterReps = parameterChild.getAccessor().getValues(theParameters);

		return parameterReps.stream()
				.filter(param -> {
					BaseRuntimeElementCompositeDefinition nextParameterDef =
							(BaseRuntimeElementCompositeDefinition) theCtx.getElementDefinition(param.getClass());
					BaseRuntimeChildDefinition nameChild = nextParameterDef.getChildByName("name");
					List nameValues = nameChild.getAccessor().getValues(param);
					Optional> nameValue = nameValues.stream()
							.filter(t -> t instanceof IPrimitiveType)
							.map(t -> ((IPrimitiveType) t))
							.findFirst();
					return nameValue.isPresent()
							&& theParameterName.equals(nameValue.get().getValueAsString());
				})
				.collect(Collectors.toList());
	}

	public static Optional getParameterPart(FhirContext theCtx, IBase theParameter, String theParameterName) {
		BaseRuntimeElementCompositeDefinition nextParameterDef =
				(BaseRuntimeElementCompositeDefinition) theCtx.getElementDefinition(theParameter.getClass());
		BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("part");
		List parts = valueChild.getAccessor().getValues(theParameter);

		for (IBase nextPart : parts) {
			Optional name = theCtx.newTerser().getSingleValue(nextPart, "name", IPrimitiveType.class);
			if (name.isPresent() && theParameterName.equals(name.get().getValueAsString())) {
				return Optional.of(nextPart);
			}
		}

		return Optional.empty();
	}

	public static Optional getParameterPartValue(
			FhirContext theCtx, IBase theParameter, String theParameterName) {
		Optional part = getParameterPart(theCtx, theParameter, theParameterName);
		if (part.isPresent()) {
			return theCtx.newTerser().getSingleValue(part.get(), "value[x]", IBase.class);
		} else {
			return Optional.empty();
		}
	}

	public static String getParameterPartValueAsString(
			FhirContext theCtx, IBase theParameter, String theParameterName) {
		return getParameterPartValue(theCtx, theParameter, theParameterName)
				.map(t -> (IPrimitiveType) t)
				.map(t -> t.getValueAsString())
				.orElse(null);
	}

	public static Optional getParameterPartValueAsInteger(
			FhirContext theCtx, IBase theParameter, String theParameterName) {
		return getParameterPartValue(theCtx, theParameter, theParameterName)
				.filter(t -> IPrimitiveType.class.isAssignableFrom(t.getClass()))
				.map(t -> (IPrimitiveType) t)
				.map(IPrimitiveType::getValue)
				.filter(t -> Integer.class.isAssignableFrom(t.getClass()))
				.map(t -> (Integer) t);
	}

	private static  List extractNamedParameters(
			FhirContext theCtx,
			IBaseParameters theParameters,
			String theParameterName,
			Function, T> theMapper) {
		List retVal = new ArrayList<>();

		List namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
		for (IBase nextParameter : namedParameters) {
			BaseRuntimeElementCompositeDefinition nextParameterDef =
					(BaseRuntimeElementCompositeDefinition) theCtx.getElementDefinition(nextParameter.getClass());
			BaseRuntimeChildDefinition valueChild = nextParameterDef.getChildByName("value[x]");
			List valueValues = valueChild.getAccessor().getValues(nextParameter);
			valueValues.stream()
					.filter(t -> t instanceof IPrimitiveType)
					.map(t -> ((IPrimitiveType) t))
					.map(theMapper)
					.filter(t -> t != null)
					.forEach(retVal::add);
		}
		return retVal;
	}

	private static void addClientParameter(
			FhirContext theContext,
			Object theValue,
			IBaseResource theTargetResource,
			BaseRuntimeChildDefinition paramChild,
			BaseRuntimeElementCompositeDefinition paramChildElem,
			String theName) {
		Validate.notNull(theValue, "theValue must not be null");

		if (theValue instanceof IBaseResource) {
			IBase parameter =
					createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
			paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue);
		} else if (theValue instanceof IBaseDatatype) {
			IBase parameter =
					createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
			paramChildElem.getChildByName("value[x]").getMutator().addValue(parameter, (IBaseDatatype) theValue);
		} else if (theValue instanceof Collection) {
			Collection collection = (Collection) theValue;
			for (Object next : collection) {
				addClientParameter(theContext, next, theTargetResource, paramChild, paramChildElem, theName);
			}
		} else {
			throw new IllegalArgumentException(Msg.code(1806) + "Don't know how to handle value of type "
					+ theValue.getClass() + " for parameter " + theName);
		}
	}

	/**
	 * Add a parameter value to a Parameters resource
	 *
	 * @param theContext    The FhirContext
	 * @param theParameters The Parameters resource
	 * @param theName       The parametr name
	 * @param theValue      The parameter value (can be a {@link IBaseResource resource} or a {@link IBaseDatatype datatype})
	 */
	public static void addParameterToParameters(
			FhirContext theContext, IBaseParameters theParameters, String theName, Object theValue) {
		RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
		BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
		BaseRuntimeElementCompositeDefinition paramChildElem =
				(BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter");

		addClientParameter(theContext, theValue, theParameters, paramChild, paramChildElem, theName);
	}

	/**
	 * Add a parameter value to a Parameters resource
	 *
	 * @param theContext           The FhirContext
	 * @param theParameters        The Parameters resource
	 * @param theName              The parameter name
	 * @param thePrimitiveDatatype The datatype, e.g. "string", or "uri"
	 * @param theValue             The value
	 */
	public static void addParameterToParameters(
			FhirContext theContext,
			IBaseParameters theParameters,
			String theName,
			String thePrimitiveDatatype,
			String theValue) {
		Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty");

		BaseRuntimeElementDefinition datatypeDef = theContext.getElementDefinition(thePrimitiveDatatype);
		IPrimitiveType value = (IPrimitiveType) datatypeDef.newInstance();
		value.setValueAsString(theValue);

		addParameterToParameters(theContext, theParameters, theName, value);
	}

	private static IBase createParameterRepetition(
			FhirContext theContext,
			IBaseResource theTargetResource,
			BaseRuntimeChildDefinition paramChild,
			BaseRuntimeElementCompositeDefinition paramChildElem,
			String theName) {
		IBase parameter = paramChildElem.newInstance();
		paramChild.getMutator().addValue(theTargetResource, parameter);
		IPrimitiveType value;
		value = createString(theContext, theName);
		paramChildElem.getChildByName("name").getMutator().addValue(parameter, value);
		return parameter;
	}

	public static IPrimitiveType createString(FhirContext theContext, String theValue) {
		IPrimitiveType value;
		if (theContext.getVersion().getVersion().isRi()) {
			value = (IPrimitiveType)
					theContext.getElementDefinition("string").newInstance(theValue);
		} else {
			value = new StringDt(theValue);
		}
		return value;
	}

	public static IPrimitiveType createUri(FhirContext theContext, String theValue) {
		IPrimitiveType value =
				(IPrimitiveType) theContext.getElementDefinition("uri").newInstance(theValue);
		return value;
	}

	public static IPrimitiveType createCode(FhirContext theContext, String theValue) {
		IPrimitiveType value =
				(IPrimitiveType) theContext.getElementDefinition("code").newInstance(theValue);
		return value;
	}

	public static IBaseParameters newInstance(FhirContext theContext) {
		Validate.notNull(theContext, "theContext must not be null");
		return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
	}

	@SuppressWarnings("unchecked")
	public static void addParameterToParametersBoolean(
			FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) {
		addParameterToParameters(theCtx, theParameters, theName, theCtx.getPrimitiveBoolean(theValue));
	}

	@SuppressWarnings("unchecked")
	public static void addParameterToParametersCode(
			FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
		IPrimitiveType value =
				(IPrimitiveType) theCtx.getElementDefinition("code").newInstance();
		value.setValue(theValue);
		addParameterToParameters(theCtx, theParameters, theName, value);
	}

	@SuppressWarnings("unchecked")
	public static void addParameterToParametersInteger(
			FhirContext theCtx, IBaseParameters theParameters, String theName, int theValue) {
		IPrimitiveType count =
				(IPrimitiveType) theCtx.getElementDefinition("integer").newInstance();
		count.setValue(theValue);
		addParameterToParameters(theCtx, theParameters, theName, count);
	}

	public static void addParameterToParametersLong(
			FhirContext theCtx, IBaseParameters theParameters, String theName, long theValue) {
		addParameterToParametersDecimal(theCtx, theParameters, theName, BigDecimal.valueOf(theValue));
	}

	public static void addParameterToParametersDecimal(
			FhirContext theCtx, IBaseParameters theParameters, String theName, BigDecimal theValue) {
		IPrimitiveType count = (IPrimitiveType)
				theCtx.getElementDefinition("decimal").newInstance();
		count.setValue(theValue);
		addParameterToParameters(theCtx, theParameters, theName, count);
	}

	public static void addParameterToParametersReference(
			FhirContext theCtx, IBaseParameters theParameters, String theName, String theReference) {
		IBaseReference target =
				(IBaseReference) theCtx.getElementDefinition("reference").newInstance();
		target.setReference(theReference);
		addParameterToParameters(theCtx, theParameters, theName, target);
	}

	@SuppressWarnings("unchecked")
	public static void addParameterToParametersString(
			FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
		IPrimitiveType value =
				(IPrimitiveType) theCtx.getElementDefinition("string").newInstance();
		value.setValue(theValue);
		addParameterToParameters(theCtx, theParameters, theName, value);
	}

	@SuppressWarnings("unchecked")
	public static void addParameterToParametersUri(
			FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
		IPrimitiveType value =
				(IPrimitiveType) theCtx.getElementDefinition("uri").newInstance();
		value.setValue(theValue);
		addParameterToParameters(theCtx, theParameters, theName, value);
	}

	/**
	 * Add a parameter with no value (typically because we'll be adding sub-parameters)
	 */
	public static IBase addParameterToParameters(
			FhirContext theContext, IBaseParameters theParameters, String theName) {
		RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
		BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
		BaseRuntimeElementCompositeDefinition paramChildElem =
				(BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter");

		return createParameterRepetition(theContext, theParameters, paramChild, paramChildElem, theName);
	}

	public static void addPartCode(FhirContext theContext, IBase theParameter, String theName, String theCode) {
		IPrimitiveType value =
				(IPrimitiveType) theContext.getElementDefinition("code").newInstance();
		value.setValue(theCode);

		addPart(theContext, theParameter, theName, value);
	}

	public static void addPartInteger(FhirContext theContext, IBase theParameter, String theName, Integer theInteger) {
		IPrimitiveType value = (IPrimitiveType)
				theContext.getElementDefinition("integer").newInstance();
		value.setValue(theInteger);

		addPart(theContext, theParameter, theName, value);
	}

	public static void addPartString(FhirContext theContext, IBase theParameter, String theName, String theValue) {
		IPrimitiveType value = (IPrimitiveType)
				theContext.getElementDefinition("string").newInstance();
		value.setValue(theValue);

		addPart(theContext, theParameter, theName, value);
	}

	public static void addPartUrl(FhirContext theContext, IBase theParameter, String theName, String theCode) {
		IPrimitiveType value =
				(IPrimitiveType) theContext.getElementDefinition("url").newInstance();
		value.setValue(theCode);

		addPart(theContext, theParameter, theName, value);
	}

	public static void addPartBoolean(FhirContext theContext, IBase theParameter, String theName, Boolean theValue) {
		addPart(theContext, theParameter, theName, theContext.getPrimitiveBoolean(theValue));
	}

	public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) {
		IPrimitiveType value = (IPrimitiveType)
				theContext.getElementDefinition("decimal").newInstance();
		if (theValue == null) {
			value.setValue(null);
		} else {
			BigDecimal decimalValue = BigDecimal.valueOf(theValue);
			if (decimalValue.scale() < 0) {
				decimalValue = decimalValue.setScale(0);
			}
			value.setValue(decimalValue);
		}
		addPart(theContext, theParameter, theName, value);
	}

	public static void addPartCoding(
			FhirContext theContext,
			IBase theParameter,
			String theName,
			String theSystem,
			String theCode,
			String theDisplay) {
		IBase coding = theContext.getElementDefinition("coding").newInstance();

		BaseRuntimeElementCompositeDefinition codingDef =
				(BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(coding.getClass());
		codingDef.getChildByName("system").getMutator().addValue(coding, createUri(theContext, theSystem));
		codingDef.getChildByName("code").getMutator().addValue(coding, createCode(theContext, theCode));
		codingDef.getChildByName("display").getMutator().addValue(coding, createString(theContext, theDisplay));

		addPart(theContext, theParameter, theName, coding);
	}

	public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
		BaseRuntimeElementCompositeDefinition def =
				(BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(theParameter.getClass());
		BaseRuntimeChildDefinition partChild = def.getChildByName("part");

		BaseRuntimeElementCompositeDefinition partChildElem =
				(BaseRuntimeElementCompositeDefinition) partChild.getChildByName("part");
		IBase part = partChildElem.newInstance();
		partChild.getMutator().addValue(theParameter, part);

		IPrimitiveType name = (IPrimitiveType)
				theContext.getElementDefinition("string").newInstance();
		name.setValue(theName);
		partChildElem.getChildByName("name").getMutator().addValue(part, name);

		if (theValue instanceof IBaseResource) {
			partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
		} else {
			partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
		}
	}

	public static void addPartResource(
			FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
		BaseRuntimeElementCompositeDefinition def =
				(BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(theParameter.getClass());
		BaseRuntimeChildDefinition partChild = def.getChildByName("part");

		BaseRuntimeElementCompositeDefinition partChildElem =
				(BaseRuntimeElementCompositeDefinition) partChild.getChildByName("part");
		IBase part = partChildElem.newInstance();
		partChild.getMutator().addValue(theParameter, part);

		IPrimitiveType name = (IPrimitiveType)
				theContext.getElementDefinition("string").newInstance();
		name.setValue(theName);
		partChildElem.getChildByName("name").getMutator().addValue(part, name);

		partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
	}

	public static List getNamedParameterPartAsString(
			FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
		return extractNamedParameterPartsAsString(theCtx, theParameters, thePartName, theParameterName);
	}

	// TODO KHS need to consolidate duplicated functionality that came in from different branches
	private static List extractNamedParameterPartsAsString(
			FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
		List parameterReps = getParameterReps(theCtx, theParameters);

		List retVal = new ArrayList<>();

		for (IBase nextParameter : parameterReps) {
			BaseRuntimeElementCompositeDefinition nextParameterDef =
					(BaseRuntimeElementCompositeDefinition) theCtx.getElementDefinition(nextParameter.getClass());
			Optional> nameValue = getNameValue(nextParameter, nextParameterDef);
			if (!nameValue.isPresent() || !thePartName.equals(nameValue.get().getValueAsString())) {
				continue;
			}

			BaseRuntimeChildDefinition partChild = nextParameterDef.getChildByName("part");
			List partValues = partChild.getAccessor().getValues(nextParameter);
			for (IBase partValue : partValues) {
				BaseRuntimeElementCompositeDefinition partParameterDef =
						(BaseRuntimeElementCompositeDefinition) theCtx.getElementDefinition(partValue.getClass());
				Optional> partNameValue = getNameValue(partValue, partParameterDef);
				if (!partNameValue.isPresent()
						|| !theParameterName.equals(partNameValue.get().getValueAsString())) {
					continue;
				}
				BaseRuntimeChildDefinition valueChild = partParameterDef.getChildByName("value[x]");
				List valueValues = valueChild.getAccessor().getValues(partValue);
				valueValues.stream()
						.filter(t -> t instanceof IPrimitiveType)
						.map(t -> ((IPrimitiveType) t))
						.map(t -> defaultIfBlank(t.getValueAsString(), null))
						.filter(t -> t != null)
						.forEach(retVal::add);
			}
		}
		return retVal;
	}

	private static List getParameterReps(FhirContext theCtx, IBaseParameters theParameters) {
		Validate.notNull(theParameters, "theParameters must not be null");
		RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
		BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
		return parameterChild.getAccessor().getValues(theParameters);
	}

	private static Optional> getNameValue(
			IBase nextParameter, BaseRuntimeElementCompositeDefinition theNextParameterDef) {
		BaseRuntimeChildDefinition nameChild = theNextParameterDef.getChildByName("name");
		List nameValues = nameChild.getAccessor().getValues(nextParameter);
		return nameValues.stream()
				.filter(t -> t instanceof IPrimitiveType)
				.map(t -> ((IPrimitiveType) t))
				.findFirst();
	}

	@Nullable
	public static String extractDescription(AnnotatedElement theType) {
		Description description = theType.getAnnotation(Description.class);
		if (description != null) {
			return extractDescription(description);
		} else {
			return null;
		}
	}

	@Nullable
	public static String extractDescription(Description desc) {
		String description = desc.value();
		if (isBlank(description)) {
			description = desc.formalDefinition();
		}
		if (isBlank(description)) {
			description = desc.shortDefinition();
		}
		return defaultIfBlank(description, null);
	}

	@Nullable
	public static String extractShortDefinition(AnnotatedElement theType) {
		Description description = theType.getAnnotation(Description.class);
		if (description != null) {
			return defaultIfBlank(description.shortDefinition(), null);
		} else {
			return null;
		}
	}

	public static String extractDescription(Annotation[] theParameterAnnotations) {
		for (Annotation next : theParameterAnnotations) {
			if (next instanceof Description) {
				return extractDescription((Description) next);
			}
		}
		return null;
	}

	public static List extractExamples(Annotation[] theParameterAnnotations) {
		ArrayList retVal = null;
		for (Annotation next : theParameterAnnotations) {
			if (next instanceof Description) {
				String[] examples = ((Description) next).example();
				if (examples.length > 0) {
					if (retVal == null) {
						retVal = new ArrayList<>();
					}
					retVal.addAll(Arrays.asList(examples));
				}
			}
		}
		return retVal;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy