From c1728a481db5e2ab7acf8815dc632c9ddb64fd10 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 6 Aug 2019 18:27:45 -0400 Subject: [PATCH] Refactor R4 infrastructure to use the new common TerminologyServiceOptions, and tweak the validation of enableWhen to handle a few edge cases correctly. --- .../fhir/dstu3/model/Base64BinaryType.java | 4 +- .../fhir/r4/conformance/ProfileUtilities.java | 2 +- .../fhir/r4/context/BaseWorkerContext.java | 52 +--- .../hl7/fhir/r4/context/IWorkerContext.java | 8 +- .../hl7/fhir/r4/context/TerminologyCache.java | 2 +- .../TerminologyServiceOptions.java | 57 ---- .../terminologies/ValueSetCheckerSimple.java | 32 +- .../org/hl7/fhir/r4/utils/FHIRPathEngine.java | 2 +- .../hl7/fhir/r4/utils/NarrativeGenerator.java | 294 ++++++------------ .../fhir/r4/utils/StructureMapUtilities.java | 6 +- .../org/hl7/fhir/r5/model/BaseResource.java | 2 +- .../utilities/TerminologyServiceOptions.java | 2 +- .../fhir/r4/validation/InstanceValidator.java | 4 +- .../r5/validation/EnableWhenEvaluator.java | 10 + .../fhir/r5/validation/InstanceValidator.java | 189 +++++------ 15 files changed, 252 insertions(+), 414 deletions(-) delete mode 100644 org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/TerminologyServiceOptions.java diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base64BinaryType.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base64BinaryType.java index 27ef75739..4b382ff2a 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base64BinaryType.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Base64BinaryType.java @@ -37,9 +37,9 @@ package org.hl7.fhir.dstu3.model; * 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. diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/conformance/ProfileUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/conformance/ProfileUtilities.java index 67da9e36d..e6b55fda7 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/conformance/ProfileUtilities.java @@ -84,7 +84,6 @@ import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.utils.NarrativeGenerator; import org.hl7.fhir.r4.utils.ToolingExtensions; @@ -92,6 +91,7 @@ import org.hl7.fhir.r4.utils.TranslatingUtilities; import org.hl7.fhir.r4.utils.formats.CSVWriter; import org.hl7.fhir.r4.utils.formats.XLSXWriter; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java index 0f4818152..4b36dd084 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java @@ -20,16 +20,7 @@ package org.hl7.fhir.r4.context; * #L% */ -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.google.gson.JsonObject; import org.apache.commons.lang3.StringUtils; import org.fhir.ucum.UcumService; import org.hl7.fhir.exceptions.DefinitionException; @@ -37,51 +28,34 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r4.conformance.ProfileUtilities; import org.hl7.fhir.r4.context.TerminologyCache.CacheToken; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.CapabilityStatement; -import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.Constants; import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; -import org.hl7.fhir.r4.model.ImplementationGuide; -import org.hl7.fhir.r4.model.MetadataResource; -import org.hl7.fhir.r4.model.NamingSystem; import org.hl7.fhir.r4.model.NamingSystem.NamingSystemIdentifierType; import org.hl7.fhir.r4.model.NamingSystem.NamingSystemUniqueIdComponent; -import org.hl7.fhir.r4.model.OperationDefinition; -import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; -import org.hl7.fhir.r4.model.PlanDefinition; -import org.hl7.fhir.r4.model.Questionnaire; -import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r4.model.SearchParameter; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.StructureDefinition; -import org.hl7.fhir.r4.model.StructureMap; -import org.hl7.fhir.r4.model.TerminologyCapabilities; import org.hl7.fhir.r4.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent; -import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.r4.terminologies.TerminologyClient; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.terminologies.ValueSetCheckerSimple; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.utilities.OIDUtils; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import com.google.gson.JsonObject; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseWorkerContext implements IWorkerContext { @@ -527,7 +501,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { if (implySystem) pIn.addParameter().setName("implySystem").setValue(new BooleanType(true)); if (options != null) - options.updateParameters(pIn); + updateParameters(options, pIn); res = validateOnServer(vs, pIn); } catch (Exception e) { res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId()); @@ -537,6 +511,12 @@ public abstract class BaseWorkerContext implements IWorkerContext { return res; } + public void updateParameters(TerminologyServiceOptions options, Parameters pIn) { + if (isNotBlank(options.getLanguage())) { + pIn.addParameter("displayLanguage", options.getLanguage()); + } + } + @Override public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) { CacheToken cacheToken = txCache.generateValidationToken(options, code, vs); @@ -561,7 +541,7 @@ public abstract class BaseWorkerContext implements IWorkerContext { Parameters pIn = new Parameters(); pIn.addParameter().setName("codeableConcept").setValue(code); if (options != null) - options.updateParameters(pIn); + updateParameters(options, pIn); res = validateOnServer(vs, pIn); } catch (Exception e) { res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId()); diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java index 7d30f4503..fbaaa5e20 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java @@ -44,11 +44,11 @@ import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.StructureMap; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.utils.INarrativeGenerator; import org.hl7.fhir.r4.utils.IResourceValidator; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -160,11 +160,6 @@ public interface IWorkerContext { * It's an error if the second form doesn't agree with class_. It's an * error if class_ is null for the last form * - * @param resource - * @param Reference - * @return - * @throws FHIRException - * @throws Exception */ public T fetchResource(Class class_, String uri); public T fetchResourceWithException(Class class_, String uri) throws FHIRException; @@ -267,7 +262,6 @@ public interface IWorkerContext { /** * ValueSet Expansion - see $expand, but resolves the binding first * - * @param source * @return * @throws FHIRException */ diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java index 071689e28..b5f7df446 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/TerminologyCache.java @@ -43,10 +43,10 @@ import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/TerminologyServiceOptions.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/TerminologyServiceOptions.java deleted file mode 100644 index e9c569c39..000000000 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/TerminologyServiceOptions.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.hl7.fhir.r4.terminologies; - -/*- - * #%L - * org.hl7.fhir.r4 - * %% - * Copyright (C) 2014 - 2019 Health Level 7 - * %% - * 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% - */ - - -import org.fhir.ucum.Utilities; -import org.hl7.fhir.r4.model.Parameters; - -public class TerminologyServiceOptions { - private String language; - - public TerminologyServiceOptions() { - super(); - } - - public TerminologyServiceOptions(String language) { - super(); - this.language = language; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String toJson() { - return "\"lang\":\""+language+"\""; - } - - public void updateParameters(Parameters pIn) { - if (!Utilities.noString(language)) - pIn.addParameter("displayLanguage", language); - - } - -} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java index 0c8587bbe..2550d737c 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/terminologies/ValueSetCheckerSimple.java @@ -21,31 +21,23 @@ package org.hl7.fhir.r4.terminologies; */ +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.context.IWorkerContext; +import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionDesignationComponent; +import org.hl7.fhir.r4.model.ValueSet.*; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.TerminologyServiceOptions; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r4.context.IWorkerContext; -import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult; -import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; -import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionDesignationComponent; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.UriType; -import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceDesignationComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent; -import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; - public class ValueSetCheckerSimple implements ValueSetChecker { private ValueSet valueset; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java index da790d870..35adf068c 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java @@ -17,9 +17,9 @@ import org.hl7.fhir.r4.model.ExpressionNode.*; import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r4.model.TypeDetails.ProfiledType; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; import java.math.BigDecimal; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NarrativeGenerator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NarrativeGenerator.java index 6dd6a93db..b2c35680f 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/NarrativeGenerator.java @@ -21,10 +21,65 @@ package org.hl7.fhir.r4.utils; */ +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.commons.lang3.NotImplementedException; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.exceptions.TerminologyServiceException; +import org.hl7.fhir.r4.conformance.ProfileUtilities; +import org.hl7.fhir.r4.conformance.ProfileUtilities.ProfileKnowledgeProvider; +import org.hl7.fhir.r4.context.IWorkerContext; +import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r4.formats.FormatUtilities; +import org.hl7.fhir.r4.formats.IParser.OutputStyle; +import org.hl7.fhir.r4.formats.XmlParser; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle.*; +import org.hl7.fhir.r4.model.CapabilityStatement.*; +import org.hl7.fhir.r4.model.CodeSystem.*; +import org.hl7.fhir.r4.model.CompartmentDefinition.CompartmentDefinitionResourceComponent; +import org.hl7.fhir.r4.model.Composition.SectionComponent; +import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r4.model.ConceptMap.OtherElementComponent; +import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r4.model.Enumeration; +import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r4.model.HumanName.NameUse; +import org.hl7.fhir.r4.model.Narrative.NarrativeStatus; +import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; +import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent; +import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r4.model.Timing.EventTiming; +import org.hl7.fhir.r4.model.Timing.TimingRepeatComponent; +import org.hl7.fhir.r4.model.Timing.UnitsOfTime; +import org.hl7.fhir.r4.model.ValueSet.FilterOperator; +import org.hl7.fhir.r4.model.ValueSet.*; +import org.hl7.fhir.r4.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; +import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; +import org.hl7.fhir.r4.utils.LiquidEngine.LiquidDocument; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.MarkDownProcessor; +import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; +import org.hl7.fhir.utilities.TerminologyServiceOptions; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.xhtml.NodeType; +import org.hl7.fhir.utilities.xhtml.XhtmlComposer; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import org.hl7.fhir.utilities.xhtml.XhtmlParser; +import org.hl7.fhir.utilities.xml.XMLUtil; +import org.hl7.fhir.utilities.xml.XmlGenerator; +import org.w3c.dom.Element; + import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.*; /* Copyright (c) 2011+, HL7, Inc @@ -55,143 +110,6 @@ Copyright (c) 2011+, HL7, Inc */ -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.LinkedTransferQueue; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.apache.commons.lang3.NotImplementedException; -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.exceptions.TerminologyServiceException; -import org.hl7.fhir.r4.conformance.ProfileUtilities; -import org.hl7.fhir.r4.conformance.ProfileUtilities.ProfileKnowledgeProvider; -import org.hl7.fhir.r4.context.IWorkerContext; -import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult; -import org.hl7.fhir.r4.formats.FormatUtilities; -import org.hl7.fhir.r4.formats.IParser.OutputStyle; -import org.hl7.fhir.r4.formats.XmlParser; -import org.hl7.fhir.r4.model.Address; -import org.hl7.fhir.r4.model.Annotation; -import org.hl7.fhir.r4.model.Attachment; -import org.hl7.fhir.r4.model.Base; -import org.hl7.fhir.r4.model.Base64BinaryType; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent; -import org.hl7.fhir.r4.model.Bundle.BundleEntryResponseComponent; -import org.hl7.fhir.r4.model.Bundle.BundleEntrySearchComponent; -import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.hl7.fhir.r4.model.CapabilityStatement; -import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent; -import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent; -import org.hl7.fhir.r4.model.CapabilityStatement.ResourceInteractionComponent; -import org.hl7.fhir.r4.model.CapabilityStatement.SystemInteractionComponent; -import org.hl7.fhir.r4.model.CapabilityStatement.SystemRestfulInteraction; -import org.hl7.fhir.r4.model.CapabilityStatement.TypeRestfulInteraction; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.r4.model.CodeSystem.CodeSystemFilterComponent; -import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; -import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionDesignationComponent; -import org.hl7.fhir.r4.model.CodeSystem.PropertyComponent; -import org.hl7.fhir.r4.model.CodeType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.CompartmentDefinition; -import org.hl7.fhir.r4.model.CompartmentDefinition.CompartmentDefinitionResourceComponent; -import org.hl7.fhir.r4.model.Composition; -import org.hl7.fhir.r4.model.Composition.SectionComponent; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; -import org.hl7.fhir.r4.model.ConceptMap.OtherElementComponent; -import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent; -import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent; -import org.hl7.fhir.r4.model.ContactDetail; -import org.hl7.fhir.r4.model.ContactPoint; -import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.DiagnosticReport; -import org.hl7.fhir.r4.model.DomainResource; -import org.hl7.fhir.r4.model.Dosage; -import org.hl7.fhir.r4.model.ElementDefinition; -import org.hl7.fhir.r4.model.Enumeration; -import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.ExtensionHelper; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.HumanName.NameUse; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.ImplementationGuide; -import org.hl7.fhir.r4.model.InstantType; -import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.MetadataResource; -import org.hl7.fhir.r4.model.Narrative; -import org.hl7.fhir.r4.model.Narrative.NarrativeStatus; -import org.hl7.fhir.r4.model.OperationDefinition; -import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent; -import org.hl7.fhir.r4.model.OperationOutcome; -import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; -import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent; -import org.hl7.fhir.r4.model.Period; -import org.hl7.fhir.r4.model.PrimitiveType; -import org.hl7.fhir.r4.model.Property; -import org.hl7.fhir.r4.model.Quantity; -import org.hl7.fhir.r4.model.Questionnaire; -import org.hl7.fhir.r4.model.Range; -import org.hl7.fhir.r4.model.Ratio; -import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.RelatedArtifact; -import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r4.model.SampledData; -import org.hl7.fhir.r4.model.Signature; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.StructureDefinition; -import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; -import org.hl7.fhir.r4.model.Timing; -import org.hl7.fhir.r4.model.Timing.EventTiming; -import org.hl7.fhir.r4.model.Timing.TimingRepeatComponent; -import org.hl7.fhir.r4.model.Timing.UnitsOfTime; -import org.hl7.fhir.r4.model.Type; -import org.hl7.fhir.r4.model.UriType; -import org.hl7.fhir.r4.model.UsageContext; -import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceDesignationComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent; -import org.hl7.fhir.r4.model.ValueSet.FilterOperator; -import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; -import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionParameterComponent; -import org.hl7.fhir.r4.terminologies.CodeSystemUtilities; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; -import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; -import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r4.utils.LiquidEngine.LiquidDocument; -import org.hl7.fhir.r4.utils.NarrativeGenerator.ILiquidTemplateProvider; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.MarkDownProcessor; -import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.xhtml.NodeType; -import org.hl7.fhir.utilities.xhtml.XhtmlComposer; -import org.hl7.fhir.utilities.xhtml.XhtmlNode; -import org.hl7.fhir.utilities.xhtml.XhtmlParser; -import org.hl7.fhir.utilities.xml.XMLUtil; -import org.hl7.fhir.utilities.xml.XmlGenerator; -import org.w3c.dom.Element; - public class NarrativeGenerator implements INarrativeGenerator { public interface ILiquidTemplateProvider { @@ -201,7 +119,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } public interface ITypeParser { - Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException ; + Base parseType(String xml, String type) throws IOException, FHIRException ; } public class ConceptMapRenderInstructions { @@ -389,29 +307,29 @@ public class NarrativeGenerator implements INarrativeGenerator { } private interface PropertyWrapper { - public String getName(); - public boolean hasValues(); - public List getValues(); - public String getTypeCode(); - public String getDefinition(); - public int getMinCardinality(); - public int getMaxCardinality(); - public StructureDefinition getStructure(); - public BaseWrapper value(); + String getName(); + boolean hasValues(); + List getValues(); + String getTypeCode(); + String getDefinition(); + int getMinCardinality(); + int getMaxCardinality(); + StructureDefinition getStructure(); + BaseWrapper value(); } private interface ResourceWrapper { - public List getContained(); - public String getId(); - public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException; - public String getName(); - public List children(); + List getContained(); + String getId(); + XhtmlNode getNarrative() throws IOException, FHIRException; + String getName(); + List children(); } private interface BaseWrapper { - public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException; - public List children(); - public PropertyWrapper getChildByName(String tail); + Base getBase() throws IOException, FHIRException; + List children(); + PropertyWrapper getChildByName(String tail); } private class BaseWrapperElement implements BaseWrapper { @@ -430,7 +348,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } @Override - public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException { + public Base getBase() throws IOException, FHIRException { if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element")) return null; @@ -578,7 +496,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } @Override - public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException { + public Base getBase() throws IOException, FHIRException { if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element")) return null; @@ -648,7 +566,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } @Override - public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException { + public XhtmlNode getNarrative() throws IOException, FHIRException { org.hl7.fhir.r4.elementmodel.Element txt = wrapped.getNamedChild("text"); if (txt == null) return null; @@ -781,7 +699,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } @Override - public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException { + public XhtmlNode getNarrative() throws IOException, FHIRException { Element txt = XMLUtil.getNamedChild(wrapped, "text"); if (txt == null) return null; @@ -1140,7 +1058,7 @@ public class NarrativeGenerator implements INarrativeGenerator { return b; } - private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException { + private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, IOException { ResourceWrapperElement resw = new ResourceWrapperElement(eres, profile); BaseWrapperElement base = new BaseWrapperElement(ee, null, profile, profile.getSnapshot().getElement().get(0)); @@ -1148,11 +1066,11 @@ public class NarrativeGenerator implements INarrativeGenerator { } - private void generateByProfile(Resource res, StructureDefinition profile, Base e, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private void generateByProfile(Resource res, StructureDefinition profile, Base e, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException { generateByProfile(new ResourceWrapperDirect(res), profile, new BaseWrapperDirect(e), allElements, defn, children, x, path, showCodeDetails, 0, rc); } - private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails, int indent, ResourceContext rc) throws FHIRException, IOException { if (children.isEmpty()) { renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent, rc); } else { @@ -1224,7 +1142,7 @@ public class NarrativeGenerator implements INarrativeGenerator { i++; if (i > 6) i = 6; - return "h"+Integer.toString(i); + return "h"+ i; } private void filterGrandChildren(List grandChildren, String string, PropertyWrapper prop) { @@ -1242,7 +1160,7 @@ public class NarrativeGenerator implements INarrativeGenerator { grandChildren.removeAll(toRemove); } - private List splitExtensions(StructureDefinition profile, List children) throws UnsupportedEncodingException, IOException, FHIRException { + private List splitExtensions(StructureDefinition profile, List children) throws IOException, FHIRException { List results = new ArrayList(); Map map = new HashMap(); for (PropertyWrapper p : children) @@ -1278,7 +1196,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } @SuppressWarnings("rawtypes") - private boolean isDefaultValue(Map displayHints, List list) throws UnsupportedEncodingException, IOException, FHIRException { + private boolean isDefaultValue(Map displayHints, List list) throws IOException, FHIRException { if (list.size() != 1) return false; if (list.get(0).getBase() instanceof PrimitiveType) @@ -1289,9 +1207,7 @@ public class NarrativeGenerator implements INarrativeGenerator { private boolean isDefault(Map displayHints, PrimitiveType primitiveType) { String v = primitiveType.asStringValue(); - if (!Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default"))) - return true; - return false; + return !Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default")); } private boolean exemptFromRendering(ElementDefinition child) { @@ -1299,16 +1215,13 @@ public class NarrativeGenerator implements INarrativeGenerator { return false; if ("Composition.subject".equals(child.getPath())) return true; - if ("Composition.section".equals(child.getPath())) - return true; - return false; + return "Composition.section".equals(child.getPath()); } private boolean renderAsList(ElementDefinition child) { if (child.getType().size() == 1) { String t = child.getType().get(0).getCode(); - if (t.equals("Address") || t.equals("Reference")) - return true; + return t.equals("Address") || t.equals("Reference"); } return false; } @@ -1318,7 +1231,7 @@ public class NarrativeGenerator implements INarrativeGenerator { tr.td().b().addText(Utilities.capitalize(tail(e.getPath()))); } - private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List grandChildren, BaseWrapper v, boolean showCodeDetails, Map displayHints, String path, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List grandChildren, BaseWrapper v, boolean showCodeDetails, Map displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException { for (ElementDefinition e : grandChildren) { PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1)); if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null) @@ -1361,9 +1274,7 @@ public class NarrativeGenerator implements INarrativeGenerator { //we can tell if e is a primitive because it has types if (e.getType().isEmpty()) return false; - if (e.getType().size() == 1 && isBase(e.getType().get(0).getCode())) - return false; - return true; + return e.getType().size() != 1 || !isBase(e.getType().get(0).getCode()); // return !e.getType().isEmpty() } @@ -1380,7 +1291,7 @@ public class NarrativeGenerator implements INarrativeGenerator { return null; } - private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map displayHints, String path, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException { if (ew == null) return; @@ -1486,7 +1397,7 @@ public class NarrativeGenerator implements INarrativeGenerator { } } - private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException { if (ew == null) return false; Base e = ew.getBase(); @@ -1640,7 +1551,7 @@ public class NarrativeGenerator implements INarrativeGenerator { return s + (!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay()); } - private void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { + private void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, ResourceContext rc) throws FHIRException, IOException { if (!textAlready) { XhtmlNode div = res.getNarrative(); if (div != null) { @@ -1687,8 +1598,7 @@ public class NarrativeGenerator implements INarrativeGenerator { return true; if (child.getType().size() == 1) { String t = child.getType().get(0).getCode(); - if (t.equals("Address") || t.equals("Contact") || t.equals("Reference") || t.equals("Uri") || t.equals("Url") || t.equals("Canonical")) - return false; + return !t.equals("Address") && !t.equals("Contact") && !t.equals("Reference") && !t.equals("Uri") && !t.equals("Url") && !t.equals("Canonical"); } return true; } @@ -2048,14 +1958,14 @@ public class NarrativeGenerator implements INarrativeGenerator { if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart()) b.append("Starting "+rep.getBoundsPeriod().getStartElement().toHumanDisplay()); if (rep.hasCount()) - b.append("Count "+Integer.toString(rep.getCount())+" times"); + b.append("Count "+ rep.getCount() +" times"); if (rep.hasDuration()) b.append("Duration "+rep.getDuration().toPlainString()+displayTimeUnits(rep.getPeriodUnit())); if (rep.hasWhen()) { String st = ""; if (rep.hasOffset()) { - st = Integer.toString(rep.getOffset())+"min "; + st = rep.getOffset() +"min "; } b.append("Do "+st); for (Enumeration wh : rep.getWhen()) @@ -2067,7 +1977,7 @@ public class NarrativeGenerator implements INarrativeGenerator { else { st = Integer.toString(rep.getFrequency()); if (rep.hasFrequencyMax()) - st = st + "-"+Integer.toString(rep.getFrequency()); + st = st + "-"+ rep.getFrequency(); } if (rep.hasPeriod()) { st = st + " per "+rep.getPeriod().toPlainString(); @@ -3046,7 +2956,7 @@ public class NarrativeGenerator implements INarrativeGenerator { if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { String l = ToolingExtensions.readStringExtension(ext, "lang"); if (lang.equals(l)) - d = ToolingExtensions.readStringExtension(ext, "content");; + d = ToolingExtensions.readStringExtension(ext, "content"); } } tr.td().addText(d == null ? "" : d); @@ -3130,9 +3040,7 @@ public class NarrativeGenerator implements INarrativeGenerator { private boolean checkDoSystem(ValueSet vs, ValueSet src) { if (src != null) vs = src; - if (vs.hasCompose()) - return true; - return false; + return vs.hasCompose(); } private boolean IsNotFixedExpansion(ValueSet vs) { @@ -4118,7 +4026,7 @@ public class NarrativeGenerator implements INarrativeGenerator { tr = tbl.tr(); tr.td().addText(p.getUse().toString()); tr.td().addText(path+p.getName()); - tr.td().addText(Integer.toString(p.getMin())+".."+p.getMax()); + tr.td().addText(p.getMin() +".."+p.getMax()); XhtmlNode td = tr.td(); StructureDefinition sd = context.fetchTypeDefinition(p.getType()); if (sd != null) @@ -4342,7 +4250,7 @@ public class NarrativeGenerator implements INarrativeGenerator { for (SectionComponent section : sections) { node.hr(); if (section.hasTitleElement()) - node.addTag("h"+Integer.toString(level)).addText(section.getTitle()); + node.addTag("h"+ level).addText(section.getTitle()); // else if (section.hasCode()) // node.addTag("h"+Integer.toString(level)).addText(displayCodeableConcept(section.getCode())); @@ -4611,7 +4519,7 @@ public class NarrativeGenerator implements INarrativeGenerator { if (be.hasResource() && be.getResource().hasId()) root.an(be.getResource().getResourceType().name().toLowerCase() + "_" + be.getResource().getId()); root.hr(); - root.para().addText("Entry "+Integer.toString(i)+(be.hasFullUrl() ? " - Full URL = " + be.getFullUrl() : "")); + root.para().addText("Entry "+ i +(be.hasFullUrl() ? " - Full URL = " + be.getFullUrl() : "")); if (be.hasRequest()) renderRequest(root, be.getRequest()); if (be.hasSearch()) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java index e78bc3e36..631c078c1 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java @@ -102,11 +102,11 @@ import org.hl7.fhir.r4.model.TypeDetails.ProfiledType; import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.xhtml.NodeType; @@ -120,7 +120,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode; * getTargetType(map) - return the definition for the type to create to hand in * transform(appInfo, source, map, target) - transform from source to target following the map * analyse(appInfo, map) - generate profiles and other analysis artifacts for the targets of the transform - * map generateMapFromMappings(StructureDefinition) - build a mapping from a structure definition with loigcal mappings + * map generateMapFromMappings(StructureDefinition) - build a mapping from a structure definition with logical mappings * * @author Grahame Grieve * @@ -223,7 +223,7 @@ public class StructureMapUtilities { private ITransformerServices services; private ProfileKnowledgeProvider pkp; private Map ids = new HashMap(); - private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions(); + private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions(); public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) { super(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseResource.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseResource.java index 2f8326c89..c8218630f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseResource.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BaseResource.java @@ -48,7 +48,7 @@ public abstract class BaseResource extends Base implements IAnyResource, IElemen @Override public FhirVersionEnum getStructureFhirVersionEnum() { - return FhirVersionEnum.R4; // to: change to R5 + return FhirVersionEnum.R5; // to: change to R5 } @Override diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TerminologyServiceOptions.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TerminologyServiceOptions.java index fcaa9b5cb..a0845f20c 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TerminologyServiceOptions.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TerminologyServiceOptions.java @@ -2,7 +2,7 @@ package org.hl7.fhir.utilities; /*- * #%L - * org.hl7.fhir.r5 + * org.hl7.fhir.utilities * %% * Copyright (C) 2014 - 2019 Health Level 7 * %% diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 08efecf11..9745fa28e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -120,8 +120,8 @@ import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ValidationProfileSet; import org.hl7.fhir.r4.utils.ValidationProfileSet.ProfileRegistration; -import org.hl7.fhir.r4.terminologies.TerminologyServiceOptions; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -809,7 +809,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (ss) { t = System.nanoTime(); ValidationResult s = context.validateCode(new TerminologyServiceOptions("en"), system, code, display); - txTime = txTime + (System.nanoTime() - t); + txTime += (System.nanoTime() - t); if (s == null) return true; if (s.isOk()) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/EnableWhenEvaluator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/EnableWhenEvaluator.java index b4ed77368..ac72bc588 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/EnableWhenEvaluator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/EnableWhenEvaluator.java @@ -296,6 +296,16 @@ public class EnableWhenEvaluator { } retVal.addAll(findOnItem(item, question)); } + + // In case the question with the enableWhen is a direct child of the question with + // the answer that it depends on. There is an example of this in the + // "BO_ConsDrop" question in this test case: + // https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-validation/src/test/resources/dstu3/fmc03-questionnaire.json + if (hasLinkId(focus, question)) { + List answers = extractAnswer(focus); + retVal.addAll(answers); + } + return retVal; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java index e852c20a2..04bee756f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/r5/validation/InstanceValidator.java @@ -33,13 +33,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.Stack; import java.util.UUID; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r5.model.Reference; -import org.hl7.fhir.exceptions.*; import org.hl7.fhir.convertors.VersionConvertorConstants; import org.hl7.fhir.convertors.VersionConvertor_10_50; import org.hl7.fhir.convertors.VersionConvertor_14_50; @@ -107,7 +105,6 @@ import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.r5.model.Range; import org.hl7.fhir.r5.model.Ratio; -import org.hl7.fhir.r5.model.Reference; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.SampledData; import org.hl7.fhir.r5.model.StringType; @@ -122,7 +119,6 @@ import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; @@ -130,7 +126,6 @@ import org.hl7.fhir.r5.utils.IResourceValidator; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ValidationProfileSet; import org.hl7.fhir.r5.utils.ValidationProfileSet.ProfileRegistration; -import org.hl7.fhir.r5.utils.Version; import org.hl7.fhir.r5.validation.EnableWhenEvaluator.QStack; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TerminologyServiceOptions; @@ -3170,7 +3165,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return null; } - private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + private void validateQuestionnaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { String text = element.getNamedChildValue("text"); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId()); @@ -3191,74 +3186,76 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat for (Element answer : answers) { NodeStack ns = stack.push(answer, -1, null, null); - switch (qItem.getType()) { - case GROUP: - rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); - break; - case DISPLAY: // nothing - break; - case BOOLEAN: - validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); - break; - case DECIMAL: - validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); - break; - case INTEGER: - validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); - break; - case DATE: - validateQuestionnaireResponseItemType(errors, answer, ns, "date"); - break; - case DATETIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); - break; - case TIME: - validateQuestionnaireResponseItemType(errors, answer, ns, "time"); - break; - case STRING: - validateQuestionnaireResponseItemType(errors, answer, ns, "string"); - break; - case TEXT: - validateQuestionnaireResponseItemType(errors, answer, ns, "text"); - break; - case URL: - validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); - break; - case ATTACHMENT: - validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); - break; - case REFERENCE: - validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); - break; - case QUANTITY: - if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) - if (qItem.hasExtension("???")) - validateQuestionnaireResponseItemQuantity(errors, answer, ns); - break; - case CHOICE: - String itemType=validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); - } - break; - case OPENCHOICE: - itemType=validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); - if (itemType != null) { - if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); - else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); - else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); - else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); - else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string", true); - } - break; - case QUESTION: - case NULL: - // no validation - break; + if (qItem.getType() != null) { + switch (qItem.getType()) { + case GROUP: + rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); + break; + case DISPLAY: // nothing + break; + case BOOLEAN: + validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); + break; + case DECIMAL: + validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); + break; + case INTEGER: + validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); + break; + case DATE: + validateQuestionnaireResponseItemType(errors, answer, ns, "date"); + break; + case DATETIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); + break; + case TIME: + validateQuestionnaireResponseItemType(errors, answer, ns, "time"); + break; + case STRING: + validateQuestionnaireResponseItemType(errors, answer, ns, "string"); + break; + case TEXT: + validateQuestionnaireResponseItemType(errors, answer, ns, "text"); + break; + case URL: + validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); + break; + case ATTACHMENT: + validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); + break; + case REFERENCE: + validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); + break; + case QUANTITY: + if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) + if (qItem.hasExtension("???")) + validateQuestionnaireResponseItemQuantity(errors, answer, ns); + break; + case CHOICE: + String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); + } + break; + case OPENCHOICE: + itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); + if (itemType != null) { + if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); + else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); + else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); + else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); + else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string", true); + } + break; + case QUESTION: + case NULL: + // no validation + break; + } } validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); } @@ -3277,13 +3274,13 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; } - private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + private void validateQuestionnaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { if (elements.size() > 1) rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); int i = 0; for (Element element : elements) { NodeStack ns = stack.push(element, i, null, null); - validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); + validateQuestionnaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element)); i++; } } @@ -3311,7 +3308,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L if (qItem != null) { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); NodeStack ns = stack.push(item, -1, null, null); - validateQuestionannaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); + validateQuestionnaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item)); } else rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); @@ -3320,22 +3317,25 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L { rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); lastIndex = index; - - List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); - - mapItem.add(item); + + // If an item has a child called "linkId" but no child called "answer", + // we'll treat it as not existing for the purposes of enableWhen validation + if (item.hasChildren("answer") || item.hasChildren("item")) { + List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); + mapItem.add(item); + } } } } - // ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order + // ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order for (QuestionnaireItemComponent qItem : qItems) { List mapItem = map.get(qItem.getLinkId()); - validateQuestionannaireResponseItem(qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); + validateQuestionnaireResponseItem(qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack); } } - public void validateQuestionannaireResponseItem(Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { + public void validateQuestionnaireResponseItem(Questionnaire qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) { boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(qItem, qstack); if (mapItem != null){ if (!enabled) { @@ -3346,13 +3346,24 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L i++; } } - validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); - } else { - //item is missing, is the question enabled? - if (enabled && qItem.getRequired()) { - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "No response found for required item with id = '"+qItem.getLinkId()+"'"); + + // Recursively validate child items + validateQuestionnaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack); + + } else { + + // item is missing, is the question enabled? + if (enabled && qItem.getRequired()) { + String message = "No response found for required item with id = '" + qItem.getLinkId() + "'"; + if (inProgress) { + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } else { + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, message); + } } + } + } private String misplacedItemError(QuestionnaireItemComponent qItem) {