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

com.ibm.fhir.model.util.ValidationSupport Maven / Gradle / Ivy

/*
 * (C) Copyright IBM Corp. 2019, 2021
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package com.ibm.fhir.model.util;

import static com.ibm.fhir.model.util.FHIRUtil.REFERENCE_PATTERN;
import static com.ibm.fhir.model.util.ModelSupport.FHIR_STRING;

import java.io.StringReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import com.ibm.fhir.model.config.FHIRModelConfig;
import com.ibm.fhir.model.lang.util.LanguageRegistryUtil;
import com.ibm.fhir.model.resource.Resource;
import com.ibm.fhir.model.type.Code;
import com.ibm.fhir.model.type.CodeableConcept;
import com.ibm.fhir.model.type.Coding;
import com.ibm.fhir.model.type.Element;
import com.ibm.fhir.model.type.Extension;
import com.ibm.fhir.model.type.Quantity;
import com.ibm.fhir.model.type.Reference;
import com.ibm.fhir.model.type.Uri;
import com.ibm.fhir.model.ucum.util.UCUMUtil;

/**
 * Static helper methods for validating model objects during construction
 *
 * @apiNote In methods where an exception is thrown, IllegalStateException is chosen over IllegalArgumentException
 *          so that Builder.build() methods can throw the most appropriate exception without catching and wrapping
 */
public final class ValidationSupport {
    public static final String ALL_LANG_VALUE_SET_URL = "http://hl7.org/fhir/ValueSet/all-languages";
    public static final String UCUM_UNITS_VALUE_SET_URL = "http://hl7.org/fhir/ValueSet/ucum-units";
    public static final String BCP_47_URN = "urn:ietf:bcp:47";
    public static final String UCUM_CODE_SYSTEM_URL = "http://unitsofmeasure.org";
    public static final String DATA_ABSENT_REASON_EXTENSION_URL = "http://hl7.org/fhir/StructureDefinition/data-absent-reason";
    private static final int RESOURCE_TYPE_GROUP = 4;
    private static final int MIN_STRING_LENGTH = 1;
    private static final int MAX_STRING_LENGTH = 1048576; // 1024 * 1024 = 1MB
    private static final String LOCAL_REF_PREFIX = "urn:";
    private static final String HTTP_PREFIX = "http:";
    private static final String HTTPS_PREFIX = "https:";
    private static final String FHIR_XHTML_XSD = "fhir-xhtml.xsd";
    private static final String FHIR_XML_XSD = "xml.xsd";
    private static final String FHIR_XMLDSIG_CORE_SCHEMA_XSD = "xmldsig-core-schema.xsd";
    private static final SchemaFactory SCHEMA_FACTORY = createSchemaFactory();
    private static final Schema SCHEMA = createSchema();
    private static final ThreadLocal THREAD_LOCAL_VALIDATOR = new ThreadLocal() {
        @Override
        public Validator initialValue() {
            return SCHEMA.newValidator();
        }
    };
    private static final Set WHITESPACE = new HashSet<>(Arrays.asList(' ', '\t', '\r', '\n'));
    private static final char [] BASE64_CHARS = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
    };
    private static final Map BASE64_INDEX_MAP = buildBase64IndexMap();

    private ValidationSupport() { }

    private static Map buildBase64IndexMap() {
        Map base64IndexMap = new LinkedHashMap<>();
        for (int i = 0; i < BASE64_CHARS.length; i++) {
            base64IndexMap.put(BASE64_CHARS[i], i);
        }
        return base64IndexMap;
    }

    /**
     * A sequence of Unicode characters
     * 
     * pattern:  [ \r\n\t\S]+
     * 
* * @throws IllegalStateException if the passed String is not a valid FHIR String value */ public static void checkString(String s) { if (s == null) { return; } if (s.length() > MAX_STRING_LENGTH) { throw new IllegalStateException(String.format("String value length: %d is greater than maximum allowed length: %d", s.length(), MAX_STRING_LENGTH)); } int count = 0; for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if (!Character.isWhitespace(ch)) { count++; } else if (!WHITESPACE.contains(ch)) { throw new IllegalStateException(String.format("String value: '%s' is not valid with respect to pattern: [ \\r\\n\\t\\S]+", s)); } } if (count < MIN_STRING_LENGTH) { throw new IllegalStateException(String.format("Trimmed String value length: %d is less than minimum required length: %d", count, MIN_STRING_LENGTH)); } } /** * A string which has at least one character and no leading or trailing whitespace and where there is no whitespace other * than single spaces in the contents. *
     * pattern:  [^\s]+(\s[^\s]+)*
     * 
* * @throws IllegalStateException if the passed String is not a valid FHIR Code value */ public static void checkCode(String s) { if (s == null) { return; } if (s.length() == 0 || Character.isWhitespace(s.charAt(0))) { throw new IllegalStateException(String.format("Code value: '%s' must begin with a non-whitespace character", s)); } if (Character.isWhitespace(s.charAt(s.length() - 1))) { throw new IllegalStateException(String.format("Code value: '%s' must end with a non-whitespace character", s)); } boolean previousIsSpace = false; for (int i = 0; i < s.length(); i++) { char current = s.charAt(i); if (Character.isWhitespace(current)) { if (current != ' ') { throw new IllegalStateException(String.format("Code value: '%s' must not contain whitespace other than a single space", s)); } else if (previousIsSpace) { throw new IllegalStateException(String.format("Code value: '%s' must not contain consecutive spaces", s)); } previousIsSpace = true; } else { if (previousIsSpace) { previousIsSpace = false; } } } } /** * Any combination of letters, numerals, "-" and ".", with a length limit of 64 characters. (This might be an integer, an * unprefixed OID, UUID or any other identifier pattern that meets these constraints.) Ids are case-insensitive. *
     * pattern:  [A-Za-z0-9\-\.]{1,64}
     * 
* * @throws IllegalStateException if the passed String is not a valid FHIR Id value */ public static void checkId(String s) { if (s == null) { return; } if (s.isEmpty()) { throw new IllegalStateException(String.format("Id value must not be empty")); } if (s.length() > 64) { throw new IllegalStateException(String.format("Id value length: %d is greater than maximum allowed length: %d", s.length(), 64)); } for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); //45 = '-' //46 = '.' //48 = '0' //57 = '9' //65 = 'A' //90 = 'Z' //97 = 'a' //122 = 'z' if (c < 45 || c == 47 || (c > 57 && c < 65) || (c > 90 && c < 97) || c > 122 ) { throw new IllegalStateException(String.format("Id value: '%s' contain invalid character '%s'", s, c)); } } } /** * String of characters used to identify a name or a resource *
     * pattern:  \S*
     * 
* * @throws IllegalStateException if the passed String is not a valid FHIR uri value */ public static void checkUri(String s) { if (s == null) { return; } if (s.length() > MAX_STRING_LENGTH) { throw new IllegalStateException(String.format("Uri value length: %d is greater than maximum allowed length: %d", s.length(), MAX_STRING_LENGTH)); } for (int i = 0; i < s.length(); i++) { if (Character.isWhitespace(s.charAt(i))) { throw new IllegalStateException(String.format("Uri value: '%s' must not contain whitespace", s)); } } } /** * @throws IllegalStateException if the passed String is longer than the maximum string length */ public static void checkMaxLength(String value) { if (value != null) { if (value.length() > MAX_STRING_LENGTH) { throw new IllegalStateException(String.format("String value length: %d is greater than maximum allowed length: %d", value.length(), MAX_STRING_LENGTH)); } } } /** * @throws IllegalStateException if the passed String is shorter than the minimum string length */ public static void checkMinLength(String value) { if (value != null) { if (value.trim().length() < MIN_STRING_LENGTH) { throw new IllegalStateException(String.format("Trimmed String value length: %d is less than minimum required length: %d", value.trim().length(), MIN_STRING_LENGTH)); } } } /** * @throws IllegalStateException if the passed Integer value is less than the passed minValue */ public static void checkValue(Integer value, int minValue) { if (value != null) { if (value < minValue) { throw new IllegalStateException(String.format("Integer value: %d is less than minimum required value: %d", value, minValue)); } } } /** * @throws IllegalStateException if the passed String value does not match the passed pattern */ public static void checkValue(String value, Pattern pattern) { if (value != null) { if (!pattern.matcher(value).matches()) { throw new IllegalStateException(String.format("String value: '%s' is not valid with respect to pattern: %s", value, pattern.pattern())); } } } /** * @throws IllegalStateException if the type of the passed value is not one of the passed types */ public static T checkValueType(T value, Class... types) { if (value != null) { List> typeList = Arrays.asList(types); Class valueType = value.getClass(); if (!typeList.contains(valueType)) { List typeNameList = typeList.stream().map(Class::getSimpleName).collect(Collectors.toList()); throw new IllegalStateException(String.format("Invalid value type: %s must be one of: %s", valueType.getSimpleName(), typeNameList.toString())); } } return value; } /** * @throws IllegalStateException if the type of the passed element is not one of the passed types * @apiNote Only differs from {@link #checkValueType} in that we can provide a better error message */ public static T choiceElement(T element, String elementName, Class... types) { if (element != null) { Class elementType = element.getClass(); boolean noneMatch = true; for (Class type : types) { if (type.isAssignableFrom(elementType)) { noneMatch = false; break; } } if (noneMatch) { List typeNameList = Arrays.stream(types).map(Class::getSimpleName).collect(Collectors.toList()); throw new IllegalStateException(String.format("Invalid type: %s for choice element: '%s' must be one of: %s", elementType.getSimpleName(), elementName, typeNameList.toString())); } } return element; } /** * @throws IllegalStateException if the passed String value is not valid XHTML */ public static void checkXHTMLContent(String value) { try { Validator validator = THREAD_LOCAL_VALIDATOR.get(); validator.reset(); validator.validate(new StreamSource(new StringReader(value))); } catch (Exception e) { throw new IllegalStateException(String.format("Invalid XHTML content: %s", e.getMessage()), e); } } private static Schema createSchema() { try { StreamSource[] sources = new StreamSource[3]; sources[0] = new StreamSource(ValidationSupport.class.getClassLoader().getResourceAsStream(FHIR_XML_XSD)); sources[1] = new StreamSource(ValidationSupport.class.getClassLoader().getResourceAsStream(FHIR_XMLDSIG_CORE_SCHEMA_XSD)); sources[2] = new StreamSource(ValidationSupport.class.getClassLoader().getResourceAsStream(FHIR_XHTML_XSD)); return SCHEMA_FACTORY.newSchema(sources); } catch (Exception e) { throw new Error(e); } } private static SchemaFactory createSchemaFactory() { try { SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); return schemaFactory; } catch (Exception e) { throw new Error(e); } } /** * @throws IllegalStateException if the passed element is null or if its type is not one of the passed types */ public static T requireChoiceElement(T element, String elementName, Class... types) { requireNonNull(element, elementName); return choiceElement(element, elementName, types); } /** * @throws IllegalStateException if the passed element is null */ public static T requireNonNull(T element, String elementName) { if (element == null) { throw new IllegalStateException(String.format("Missing required element: '%s'", elementName)); } return element; } /** * @deprecated replaced by {@link #checkNonEmptyList(List, String, Class)} * @throws IllegalStateException if the passed list is empty or contains any null objects */ @Deprecated public static List requireNonEmpty(List elements, String elementName) { requireNonNull(elements, elementName); if (elements.isEmpty()) { throw new IllegalStateException(String.format("Missing required element: '%s'", elementName)); } return elements; } /** * @deprecated replaced by {@link #checkList(List, String, Class)} * @throws IllegalStateException if the passed list contains any null objects */ @Deprecated public static List requireNonNull(List elements, String elementName) { boolean anyMatch = false; for (T element : elements) { if (Objects.isNull(element)) { anyMatch = true; break; } } if (anyMatch) { throw new IllegalStateException(String.format("Repeating element: '%s' does not permit null elements", elementName)); } return elements; } /** * @return the same list that was passed * @throws IllegalStateException if the passed list is empty or contains any null objects or objects of an incompatible type */ public static List checkNonEmptyList(List elements, String elementName, Class type) { if (elements.isEmpty()) { throw new IllegalStateException(String.format("Missing required element: '%s'", elementName)); } return checkList(elements, elementName, type); } /** * @return the same list that was passed * @throws IllegalStateException if the passed list contains any null objects or objects of an incompatible type */ public static List checkList(List elements, String elementName, Class type) { for (T element : elements) { if (Objects.isNull(element)) { throw new IllegalStateException(String.format("Repeating element: '%s' does not permit null elements", elementName)); } if (!type.isInstance(element)) { throw new IllegalStateException(String.format("Invalid type: %s for repeating element: '%s' must be: %s", element.getClass().getSimpleName(), elementName, type.getSimpleName())); } } return elements; } /** * @throws IllegalStateException if the passed element has no value and no children */ public static void requireValueOrChildren(Element element) { if (!element.hasValue() && !element.hasChildren()) { throw new IllegalStateException("ele-1: All FHIR elements must have a @value or children"); } } /** * @deprecated https://jira.hl7.org/browse/FHIR-26565 has clarified that empty resources are allowed * @throws IllegalStateException if the passed element has no children */ @Deprecated public static void requireChildren(Resource resource) { if (!resource.hasChildren()) { throw new IllegalStateException("global-1: All FHIR elements must have a @value or children"); } } /** * @throws IllegalStateException if the passed element is not null */ public static void prohibited(Element element, String elementName) { if (element != null) { throw new IllegalStateException(String.format("Element: '%s' is prohibited.", elementName)); } } /** * @throws IllegalStateException if the passed list is not empty */ public static void prohibited(List elements, String elementName) { if (!elements.isEmpty()) { throw new IllegalStateException(String.format("Element: '%s' is prohibited.", elementName)); } } /** * @throws IllegalStateExeption if the codeableConcept has coding elements that do not include codes from the required binding * @deprecated use {@link #checkValueSetBinding(Element, String, String, String, String...)} */ @Deprecated public static void checkCodeableConcept(CodeableConcept codeableConcept, String elementName, String valueSet, String system, String... codes) { if (codeableConcept != null && !codeableConcept.getCoding().isEmpty() && hasCodingWithSystemAndCodeValues(codeableConcept)) { List codeList = Arrays.asList(codes); for (Coding coding : codeableConcept.getCoding()) { if (hasSystemAndCodeValues(coding) && system.equals(coding.getSystem().getValue()) && codeList.contains(coding.getCode().getValue())) { return; } } throw new IllegalStateException(String.format("Element: '%s' must contain a valid code from value set: '%s'", elementName, valueSet)); } } /** * Check that the specified list of elements contain a code that is a member of the specified value set. * * @param elements * the list of elements for which to check codes * @param elementName * the name of the element * @param valueSet * the URL of the value set to check membership against * @param system * the value set system * @param codes * the value set codes * @throws IllegalStateExeption if each element in the list does not include a code from the required value set */ public static void checkValueSetBinding(List elements, String elementName, String valueSet, String system, String... valueSetCodes) { if (elements != null) { for (Element element : elements) { checkValueSetBinding(element, elementName, valueSet, system, valueSetCodes); } } } /** * Check that the specified element contains a code that is a member of the specified value set. * * @param element * the element for which to check codes * @param elementName * the name of the element * @param valueSet * the URL of the value set to check membership against * @param system * the value set system * @param codes * the value set codes * @throws IllegalStateExeption if the element does not include a code from the required value set */ public static void checkValueSetBinding(Element element, String elementName, String valueSet, String system, String... codes) { if (element != null) { boolean extendedCodeableConceptValidation = FHIRModelConfig.getExtendedCodeableConceptValidation(); List codeList = Arrays.asList(codes); if (element instanceof CodeableConcept) { checkCodeableConcept((CodeableConcept)element, elementName, valueSet, system, codeList, extendedCodeableConceptValidation); } else if (element instanceof Coding || element instanceof Quantity) { checkCoding(element, elementName, valueSet, system, codeList, extendedCodeableConceptValidation); } else if (element instanceof Code || element instanceof Uri || element instanceof com.ibm.fhir.model.type.String) { checkCode(element, elementName, valueSet, codeList, extendedCodeableConceptValidation); } } } /** * @throws IllegalStateExeption if the CodeableConcept element does not include a code from the required binding */ private static void checkCodeableConcept(CodeableConcept codeableConcept, String elementName, String valueSet, String system, List codes, boolean extendedCodeableConceptValidation) { if (extendedCodeableConceptValidation) { if (codeableConcept.getCoding() != null) { for (Coding coding : codeableConcept.getCoding()) { try { checkCoding(coding, elementName, valueSet, system, codes, extendedCodeableConceptValidation); return; } catch (IllegalStateException e) {} } } throw new IllegalStateException(String.format("Element '%s': does not contain a Coding element with a valid system and code combination for value set: '%s'", elementName, valueSet)); } else if (!codes.isEmpty() && !codeableConcept.getCoding().isEmpty() && hasCodingWithSystemAndCodeValues(codeableConcept)) { for (Coding coding : codeableConcept.getCoding()) { if (hasSystemAndCodeValues(coding) && system.equals(coding.getSystem().getValue()) && codes.contains(coding.getCode().getValue())) { return; } } throw new IllegalStateException(String.format("Element '%s': does not contain a Coding element with a valid system and code combination for value set: '%s'", elementName, valueSet)); } } /** * @throws IllegalStateExeption if the Coding element does not include a code from the required binding */ private static void checkCoding(Element element, String elementName, String valueSet, String system, List codes, boolean extendedCodeableConceptValidation) { if (extendedCodeableConceptValidation) { if (!hasOnlyDataAbsentReasonExtension(element)) { if (hasSystemAndCodeValues(element)) { String codingSystem = null; Code codingCode = null; if (element instanceof Coding) { codingSystem = element.as(Coding.class).getSystem().getValue(); codingCode = element.as(Coding.class).getCode(); } else if (element instanceof Quantity) { codingSystem = element.as(Quantity.class).getSystem().getValue(); codingCode = element.as(Quantity.class).getCode(); } if (isSyntaxValidatedValueSet(valueSet)) { checkSyntaxValidatedCode(codingCode.getValue(), codingSystem, elementName, valueSet, extendedCodeableConceptValidation); } else { if (!codingSystem.equals(system)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid system for value set '%s'", elementName, codingSystem, valueSet)); } else { checkCode(codingCode, elementName, valueSet, codes, extendedCodeableConceptValidation); } } } else { throw new IllegalStateException(String.format("Element '%s': does not contain a valid system and code combination for value set: '%s'", elementName, valueSet)); } } } } /** * @throws IllegalStateExeption if the code element is not from the required binding */ private static void checkCode(Element element, String elementName, String valueSet, List codes, boolean extendedCodeableConceptValidation) { if (extendedCodeableConceptValidation) { if (!hasOnlyDataAbsentReasonExtension(element)) { String codeValue = null; if (element instanceof Code) { codeValue = ((Code)element).getValue(); } else if (element instanceof Uri) { codeValue = ((Uri)element).getValue(); } else if (element instanceof com.ibm.fhir.model.type.String) { codeValue = ((com.ibm.fhir.model.type.String)element).getValue(); } if (codeValue != null) { if (isSyntaxValidatedValueSet(valueSet)) { checkSyntaxValidatedCode(codeValue, null, elementName, valueSet, extendedCodeableConceptValidation); } else if (!codes.contains(codeValue)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid code for value set '%s'", elementName, codeValue, valueSet)); } } else { throw new IllegalStateException(String.format("Element '%s': does not contan a valid code for value set '%s'", elementName, valueSet)); } } } } /** * @throws IllegalStateExeption if the code element is not from the required binding */ private static void checkSyntaxValidatedCode(String code, String system, String elementName, String valueSet, boolean extendedCodeableConceptValidation) { if (extendedCodeableConceptValidation) { if (ALL_LANG_VALUE_SET_URL.contentEquals(valueSet)) { if (system != null && !BCP_47_URN.equals(system)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid system for value set '%s'", elementName, system, valueSet)); } else if (!LanguageRegistryUtil.isValidLanguageTag(code)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid code for value set '%s'", elementName, code, valueSet)); } } else if (UCUM_UNITS_VALUE_SET_URL.equals(valueSet)) { if (system != null && !UCUM_CODE_SYSTEM_URL.equals(system)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid system for value set '%s'", elementName, system, valueSet)); } else if (!UCUMUtil.isValidUcum(code)) { throw new IllegalStateException(String.format("Element '%s': '%s' is not a valid code for value set '%s'", elementName, code, valueSet)); } } } } private static boolean hasCodingWithSystemAndCodeValues(CodeableConcept codeableConcept) { for (Coding coding : codeableConcept.getCoding()) { if (hasSystemAndCodeValues(coding)) { return true; } } return false; } private static boolean hasSystemAndCodeValues(Element element) { if (element instanceof Coding) { Coding coding = (Coding) element; return coding.getSystem() != null && coding.getSystem().getValue() != null && coding.getCode() != null && coding.getCode().getValue() != null; } else if (element instanceof Quantity) { Quantity quantity = (Quantity) element; return quantity.getSystem() != null && quantity.getSystem().getValue() != null && quantity.getCode() != null && quantity.getCode().getValue() != null; } return false; } private static boolean isSyntaxValidatedValueSet(String valueSet) { if (ALL_LANG_VALUE_SET_URL.equals(valueSet) || UCUM_UNITS_VALUE_SET_URL.equals(valueSet)) { return true; } return false; } /** * @throws IllegalStateException if the resource type found in one or more reference values does not match the specified Reference.type * value or is not one of the allowed reference types for that element */ public static void checkReferenceType(List reference, String elementName, String... referenceTypes) { for (Reference r : reference) { checkReferenceType(r, elementName, referenceTypes); } } /** * @throws IllegalStateException if the choiceElement is a Reference and the reference value does not match the specified Reference.type * value or is not one of the allowed reference types for that element */ public static void checkReferenceType(Element choiceElement, String elementName, String... referenceTypes) { if (choiceElement instanceof Reference) { checkReferenceType((Reference) choiceElement, elementName, referenceTypes); } } /** * Checks that the reference contains valid resource type values. * @param reference the reference * @param elementName the element name * @param referenceTypes the valid resource types for the reference * @throws IllegalStateException if the resource type found in the reference value does not match the specified Reference.type value * or is not one of the allowed reference types for that element */ public static void checkReferenceType(Reference reference, String elementName, String... referenceTypes) { if (reference == null || !FHIRModelConfig.getCheckReferenceTypes()) { return; } String resourceType = null; String referenceReference = getReferenceReference(reference); List referenceTypeList = Arrays.asList(referenceTypes); if (referenceReference != null && !referenceReference.startsWith("#") && !referenceReference.startsWith(LOCAL_REF_PREFIX) && !referenceReference.startsWith(HTTP_PREFIX) && !referenceReference.startsWith(HTTPS_PREFIX)) { int index = referenceReference.indexOf("?"); if (index != -1) { // conditional reference resourceType = referenceReference.substring(0, index); } else { Matcher matcher = REFERENCE_PATTERN.matcher(referenceReference); if (matcher.matches()) { resourceType = matcher.group(RESOURCE_TYPE_GROUP); } } // resourceType is required in the reference value if (resourceType == null) { throw new IllegalStateException(String.format("Invalid reference value or resource type not found in reference value: '%s' for element: '%s'", referenceReference, elementName)); } if (!ModelSupport.isResourceType(resourceType)) { throw new IllegalStateException(String.format("Resource type found in reference value: '%s' for element: '%s' must be a valid resource type name", referenceReference, elementName)); } // If there is a resourceType in the reference value, check that it's an allowed value if (!referenceTypeList.contains(resourceType)) { throw new IllegalStateException(String.format("Resource type found in reference value: '%s' for element: '%s' must be one of: %s", referenceReference, elementName, referenceTypeList.toString())); } } String referenceType = getReferenceType(reference); if (referenceType != null) { if (!ModelSupport.isResourceType(referenceType)) { throw new IllegalStateException(String.format("Resource type found in Reference.type: '%s' for element: '%s' must be a valid resource type name", referenceType, elementName)); } // If there is an explicit Reference.type, ensure it's an allowed type if (!referenceTypeList.contains(referenceType)) { throw new IllegalStateException(String.format("Resource type found in Reference.type: '%s' for element: '%s' must be one of: %s", referenceType, elementName, referenceTypeList.toString())); } // If there is an explicit Reference.type, check that the resourceType pattern matches it if (resourceType != null && !resourceType.equals(referenceType)) { throw new IllegalStateException(String.format("Resource type found in reference value: '%s' for element: '%s' does not match Reference.type: %s", referenceReference, elementName, referenceType)); } } } private static String getReferenceReference(Reference reference) { if (reference.getReference() != null) { return reference.getReference().getValue(); } return null; } private static String getReferenceType(Reference reference) { if (reference.getType() != null) { return reference.getType().getValue(); } return null; } public static void validateBase64EncodedString(String value) { int length = value.length(); if ((length % 4) != 0) { throw new IllegalArgumentException("Invalid base64 string length: " + value.length()); } if (value.endsWith("==") || value.endsWith("=")) { int charIndex = value.endsWith("==") ? (length - 3) : (length - 2); char ch = value.charAt(charIndex); if (ch == '=') { throw new IllegalArgumentException("Unexpected base64 padding character: '=' found at index: " + charIndex); } int base64Index = BASE64_INDEX_MAP.getOrDefault(ch, -1); if (base64Index == -1) { throw new IllegalArgumentException("Illegal base64 character: '" + ch + "' found at index: " + charIndex); } int mask = value.endsWith("==") ? 0b001111 : 0b000011; if ((base64Index & mask) != 0) { throw new IllegalArgumentException("Invalid base64 string: non-zero padding bits; character: '" + ch + "' found at index: " + charIndex + " should be: '" + BASE64_CHARS[(base64Index & ~mask)] + "'"); } } } public static boolean hasOnlyDataAbsentReasonExtension(Element element) { if (hasDataAbsentReasonExtension(element) && ((element.is(Code.class) && element.as(Code.class).getValue() == null) || (element.is(FHIR_STRING) && element.as(FHIR_STRING).getValue() == null) || (element.is(Uri.class) && element.as(Uri.class).getValue() == null) || (element.is(Coding.class) && element.as(Coding.class).getSystem() == null && element.as(Coding.class).getCode() == null) || (element.is(Quantity.class) && element.as(Quantity.class).getSystem() == null && element.as(Quantity.class).getCode() == null))) { return true; } else if (element.is(CodeableConcept.class) && element.as(CodeableConcept.class).getCoding() != null) { for (Coding coding : element.as(CodeableConcept.class).getCoding()) { if (hasOnlyDataAbsentReasonExtension(coding)) { return true; } } } return false; } private static boolean hasDataAbsentReasonExtension(Element element) { for (Extension extension : element.getExtension()) { if (DATA_ABSENT_REASON_EXTENSION_URL.equals(extension.getUrl())) { return true; } } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy