Refactor R4 infrastructure to use the new common

TerminologyServiceOptions, and tweak the validation of enableWhen to
handle a few edge cases correctly.
This commit is contained in:
James Agnew 2019-08-06 18:27:45 -04:00
parent 38a0877996
commit c1728a481d
15 changed files with 252 additions and 414 deletions

View File

@ -37,9 +37,9 @@ package org.hl7.fhir.dstu3.model;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

View File

@ -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;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; 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.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.NarrativeGenerator; import org.hl7.fhir.r4.utils.NarrativeGenerator;
import org.hl7.fhir.r4.utils.ToolingExtensions; 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.CSVWriter;
import org.hl7.fhir.r4.utils.formats.XLSXWriter; import org.hl7.fhir.r4.utils.formats.XLSXWriter;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationMessage.Source;

View File

@ -20,16 +20,7 @@ package org.hl7.fhir.r4.context;
* #L% * #L%
*/ */
import java.io.FileNotFoundException; import com.google.gson.JsonObject;
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 org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.UcumService; import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.DefinitionException; 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.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4.conformance.ProfileUtilities; import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.TerminologyCache.CacheToken; import org.hl7.fhir.r4.context.TerminologyCache.CacheToken;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; 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.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.NamingSystemIdentifierType;
import org.hl7.fhir.r4.model.NamingSystem.NamingSystemUniqueIdComponent; 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.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.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r4.terminologies.TerminologyClient; 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.ValueSetCheckerSimple;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.utilities.OIDUtils; import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 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 { public abstract class BaseWorkerContext implements IWorkerContext {
@ -527,7 +501,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
if (implySystem) if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true)); pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (options != null) if (options != null)
options.updateParameters(pIn); updateParameters(options, pIn);
res = validateOnServer(vs, pIn); res = validateOnServer(vs, pIn);
} catch (Exception e) { } catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId()); 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; return res;
} }
public void updateParameters(TerminologyServiceOptions options, Parameters pIn) {
if (isNotBlank(options.getLanguage())) {
pIn.addParameter("displayLanguage", options.getLanguage());
}
}
@Override @Override
public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) { public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(options, code, vs); CacheToken cacheToken = txCache.generateValidationToken(options, code, vs);
@ -561,7 +541,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
Parameters pIn = new Parameters(); Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code); pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null) if (options != null)
options.updateParameters(pIn); updateParameters(options, pIn);
res = validateOnServer(vs, pIn); res = validateOnServer(vs, pIn);
} catch (Exception e) { } catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId()); res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());

View File

@ -44,11 +44,11 @@ import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureMap; import org.hl7.fhir.r4.model.StructureMap;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; 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.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.INarrativeGenerator; import org.hl7.fhir.r4.utils.INarrativeGenerator;
import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 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 * 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 * error if class_ is null for the last form
* *
* @param resource
* @param Reference
* @return
* @throws FHIRException
* @throws Exception
*/ */
public <T extends Resource> T fetchResource(Class<T> class_, String uri); public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException; public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
@ -267,7 +262,6 @@ public interface IWorkerContext {
/** /**
* ValueSet Expansion - see $expand, but resolves the binding first * ValueSet Expansion - see $expand, but resolves the binding first
* *
* @param source
* @return * @return
* @throws FHIRException * @throws FHIRException
*/ */

View File

@ -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.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; 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.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -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);
}
}

View File

@ -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.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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 { public class ValueSetCheckerSimple implements ValueSetChecker {
private ValueSet valueset; private ValueSet valueset;

View File

@ -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.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.model.TypeDetails.ProfiledType; 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.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -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.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*;
/* /*
Copyright (c) 2011+, HL7, Inc 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 class NarrativeGenerator implements INarrativeGenerator {
public interface ILiquidTemplateProvider { public interface ILiquidTemplateProvider {
@ -201,7 +119,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
public interface ITypeParser { 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 { public class ConceptMapRenderInstructions {
@ -389,29 +307,29 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
private interface PropertyWrapper { private interface PropertyWrapper {
public String getName(); String getName();
public boolean hasValues(); boolean hasValues();
public List<BaseWrapper> getValues(); List<BaseWrapper> getValues();
public String getTypeCode(); String getTypeCode();
public String getDefinition(); String getDefinition();
public int getMinCardinality(); int getMinCardinality();
public int getMaxCardinality(); int getMaxCardinality();
public StructureDefinition getStructure(); StructureDefinition getStructure();
public BaseWrapper value(); BaseWrapper value();
} }
private interface ResourceWrapper { private interface ResourceWrapper {
public List<ResourceWrapper> getContained(); List<ResourceWrapper> getContained();
public String getId(); String getId();
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException; XhtmlNode getNarrative() throws IOException, FHIRException;
public String getName(); String getName();
public List<PropertyWrapper> children(); List<PropertyWrapper> children();
} }
private interface BaseWrapper { private interface BaseWrapper {
public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException; Base getBase() throws IOException, FHIRException;
public List<PropertyWrapper> children(); List<PropertyWrapper> children();
public PropertyWrapper getChildByName(String tail); PropertyWrapper getChildByName(String tail);
} }
private class BaseWrapperElement implements BaseWrapper { private class BaseWrapperElement implements BaseWrapper {
@ -430,7 +348,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
@Override @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")) if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null; return null;
@ -578,7 +496,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
@Override @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")) if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
return null; return null;
@ -648,7 +566,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
@Override @Override
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException { public XhtmlNode getNarrative() throws IOException, FHIRException {
org.hl7.fhir.r4.elementmodel.Element txt = wrapped.getNamedChild("text"); org.hl7.fhir.r4.elementmodel.Element txt = wrapped.getNamedChild("text");
if (txt == null) if (txt == null)
return null; return null;
@ -781,7 +699,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
@Override @Override
public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException { public XhtmlNode getNarrative() throws IOException, FHIRException {
Element txt = XMLUtil.getNamedChild(wrapped, "text"); Element txt = XMLUtil.getNamedChild(wrapped, "text");
if (txt == null) if (txt == null)
return null; return null;
@ -1140,7 +1058,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return b; return b;
} }
private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException { private void generateByProfile(Element eres, StructureDefinition profile, Element ee, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, IOException {
ResourceWrapperElement resw = new ResourceWrapperElement(eres, profile); ResourceWrapperElement resw = new ResourceWrapperElement(eres, profile);
BaseWrapperElement base = new BaseWrapperElement(ee, null, profile, profile.getSnapshot().getElement().get(0)); 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<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { private void generateByProfile(Resource res, StructureDefinition profile, Base e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> 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); 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<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> 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<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent, ResourceContext rc) throws FHIRException, IOException {
if (children.isEmpty()) { if (children.isEmpty()) {
renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent, rc); renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent, rc);
} else { } else {
@ -1224,7 +1142,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
i++; i++;
if (i > 6) if (i > 6)
i = 6; i = 6;
return "h"+Integer.toString(i); return "h"+ i;
} }
private void filterGrandChildren(List<ElementDefinition> grandChildren, String string, PropertyWrapper prop) { private void filterGrandChildren(List<ElementDefinition> grandChildren, String string, PropertyWrapper prop) {
@ -1242,7 +1160,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
grandChildren.removeAll(toRemove); grandChildren.removeAll(toRemove);
} }
private List<PropertyWrapper> splitExtensions(StructureDefinition profile, List<PropertyWrapper> children) throws UnsupportedEncodingException, IOException, FHIRException { private List<PropertyWrapper> splitExtensions(StructureDefinition profile, List<PropertyWrapper> children) throws IOException, FHIRException {
List<PropertyWrapper> results = new ArrayList<PropertyWrapper>(); List<PropertyWrapper> results = new ArrayList<PropertyWrapper>();
Map<String, PropertyWrapper> map = new HashMap<String, PropertyWrapper>(); Map<String, PropertyWrapper> map = new HashMap<String, PropertyWrapper>();
for (PropertyWrapper p : children) for (PropertyWrapper p : children)
@ -1278,7 +1196,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private boolean isDefaultValue(Map<String, String> displayHints, List<BaseWrapper> list) throws UnsupportedEncodingException, IOException, FHIRException { private boolean isDefaultValue(Map<String, String> displayHints, List<BaseWrapper> list) throws IOException, FHIRException {
if (list.size() != 1) if (list.size() != 1)
return false; return false;
if (list.get(0).getBase() instanceof PrimitiveType) if (list.get(0).getBase() instanceof PrimitiveType)
@ -1289,9 +1207,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private boolean isDefault(Map<String, String> displayHints, PrimitiveType primitiveType) { private boolean isDefault(Map<String, String> displayHints, PrimitiveType primitiveType) {
String v = primitiveType.asStringValue(); String v = primitiveType.asStringValue();
if (!Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default"))) return !Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default"));
return true;
return false;
} }
private boolean exemptFromRendering(ElementDefinition child) { private boolean exemptFromRendering(ElementDefinition child) {
@ -1299,16 +1215,13 @@ public class NarrativeGenerator implements INarrativeGenerator {
return false; return false;
if ("Composition.subject".equals(child.getPath())) if ("Composition.subject".equals(child.getPath()))
return true; return true;
if ("Composition.section".equals(child.getPath())) return "Composition.section".equals(child.getPath());
return true;
return false;
} }
private boolean renderAsList(ElementDefinition child) { private boolean renderAsList(ElementDefinition child) {
if (child.getType().size() == 1) { if (child.getType().size() == 1) {
String t = child.getType().get(0).getCode(); String t = child.getType().get(0).getCode();
if (t.equals("Address") || t.equals("Reference")) return t.equals("Address") || t.equals("Reference");
return true;
} }
return false; return false;
} }
@ -1318,7 +1231,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
tr.td().b().addText(Utilities.capitalize(tail(e.getPath()))); tr.td().b().addText(Utilities.capitalize(tail(e.getPath())));
} }
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, UnsupportedEncodingException, IOException { private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException {
for (ElementDefinition e : grandChildren) { for (ElementDefinition e : grandChildren) {
PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1)); PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null) 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 //we can tell if e is a primitive because it has types
if (e.getType().isEmpty()) if (e.getType().isEmpty())
return false; return false;
if (e.getType().size() == 1 && isBase(e.getType().get(0).getCode())) return e.getType().size() != 1 || !isBase(e.getType().get(0).getCode());
return false;
return true;
// return !e.getType().isEmpty() // return !e.getType().isEmpty()
} }
@ -1380,7 +1291,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return null; return null;
} }
private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> 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<String, String> displayHints, String path, int indent, ResourceContext rc) throws FHIRException, IOException {
if (ew == null) if (ew == null)
return; 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) if (ew == null)
return false; return false;
Base e = ew.getBase(); Base e = ew.getBase();
@ -1640,7 +1551,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return s + (!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay()); 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) { if (!textAlready) {
XhtmlNode div = res.getNarrative(); XhtmlNode div = res.getNarrative();
if (div != null) { if (div != null) {
@ -1687,8 +1598,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
return true; return true;
if (child.getType().size() == 1) { if (child.getType().size() == 1) {
String t = child.getType().get(0).getCode(); 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 !t.equals("Address") && !t.equals("Contact") && !t.equals("Reference") && !t.equals("Uri") && !t.equals("Url") && !t.equals("Canonical");
return false;
} }
return true; return true;
} }
@ -2048,14 +1958,14 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart()) if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart())
b.append("Starting "+rep.getBoundsPeriod().getStartElement().toHumanDisplay()); b.append("Starting "+rep.getBoundsPeriod().getStartElement().toHumanDisplay());
if (rep.hasCount()) if (rep.hasCount())
b.append("Count "+Integer.toString(rep.getCount())+" times"); b.append("Count "+ rep.getCount() +" times");
if (rep.hasDuration()) if (rep.hasDuration())
b.append("Duration "+rep.getDuration().toPlainString()+displayTimeUnits(rep.getPeriodUnit())); b.append("Duration "+rep.getDuration().toPlainString()+displayTimeUnits(rep.getPeriodUnit()));
if (rep.hasWhen()) { if (rep.hasWhen()) {
String st = ""; String st = "";
if (rep.hasOffset()) { if (rep.hasOffset()) {
st = Integer.toString(rep.getOffset())+"min "; st = rep.getOffset() +"min ";
} }
b.append("Do "+st); b.append("Do "+st);
for (Enumeration<EventTiming> wh : rep.getWhen()) for (Enumeration<EventTiming> wh : rep.getWhen())
@ -2067,7 +1977,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
else { else {
st = Integer.toString(rep.getFrequency()); st = Integer.toString(rep.getFrequency());
if (rep.hasFrequencyMax()) if (rep.hasFrequencyMax())
st = st + "-"+Integer.toString(rep.getFrequency()); st = st + "-"+ rep.getFrequency();
} }
if (rep.hasPeriod()) { if (rep.hasPeriod()) {
st = st + " per "+rep.getPeriod().toPlainString(); st = st + " per "+rep.getPeriod().toPlainString();
@ -3046,7 +2956,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
String l = ToolingExtensions.readStringExtension(ext, "lang"); String l = ToolingExtensions.readStringExtension(ext, "lang");
if (lang.equals(l)) if (lang.equals(l))
d = ToolingExtensions.readStringExtension(ext, "content");; d = ToolingExtensions.readStringExtension(ext, "content");
} }
} }
tr.td().addText(d == null ? "" : d); tr.td().addText(d == null ? "" : d);
@ -3130,9 +3040,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private boolean checkDoSystem(ValueSet vs, ValueSet src) { private boolean checkDoSystem(ValueSet vs, ValueSet src) {
if (src != null) if (src != null)
vs = src; vs = src;
if (vs.hasCompose()) return vs.hasCompose();
return true;
return false;
} }
private boolean IsNotFixedExpansion(ValueSet vs) { private boolean IsNotFixedExpansion(ValueSet vs) {
@ -4118,7 +4026,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
tr = tbl.tr(); tr = tbl.tr();
tr.td().addText(p.getUse().toString()); tr.td().addText(p.getUse().toString());
tr.td().addText(path+p.getName()); 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(); XhtmlNode td = tr.td();
StructureDefinition sd = context.fetchTypeDefinition(p.getType()); StructureDefinition sd = context.fetchTypeDefinition(p.getType());
if (sd != null) if (sd != null)
@ -4342,7 +4250,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
for (SectionComponent section : sections) { for (SectionComponent section : sections) {
node.hr(); node.hr();
if (section.hasTitleElement()) if (section.hasTitleElement())
node.addTag("h"+Integer.toString(level)).addText(section.getTitle()); node.addTag("h"+ level).addText(section.getTitle());
// else if (section.hasCode()) // else if (section.hasCode())
// node.addTag("h"+Integer.toString(level)).addText(displayCodeableConcept(section.getCode())); // 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()) if (be.hasResource() && be.getResource().hasId())
root.an(be.getResource().getResourceType().name().toLowerCase() + "_" + be.getResource().getId()); root.an(be.getResource().getResourceType().name().toLowerCase() + "_" + be.getResource().getId());
root.hr(); 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()) if (be.hasRequest())
renderRequest(root, be.getRequest()); renderRequest(root, be.getRequest());
if (be.hasSearch()) if (be.hasSearch())

View File

@ -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.UriType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; 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.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xhtml.NodeType; 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 * 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 * 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 * 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 * @author Grahame Grieve
* *
@ -223,7 +223,7 @@ public class StructureMapUtilities {
private ITransformerServices services; private ITransformerServices services;
private ProfileKnowledgeProvider pkp; private ProfileKnowledgeProvider pkp;
private Map<String, Integer> ids = new HashMap<String, Integer>(); private Map<String, Integer> ids = new HashMap<String, Integer>();
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions(); private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) { public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) {
super(); super();

View File

@ -48,7 +48,7 @@ public abstract class BaseResource extends Base implements IAnyResource, IElemen
@Override @Override
public FhirVersionEnum getStructureFhirVersionEnum() { public FhirVersionEnum getStructureFhirVersionEnum() {
return FhirVersionEnum.R4; // to: change to R5 return FhirVersionEnum.R5; // to: change to R5
} }
@Override @Override

View File

@ -2,7 +2,7 @@ package org.hl7.fhir.utilities;
/*- /*-
* #%L * #%L
* org.hl7.fhir.r5 * org.hl7.fhir.utilities
* %% * %%
* Copyright (C) 2014 - 2019 Health Level 7 * Copyright (C) 2014 - 2019 Health Level 7
* %% * %%

View File

@ -120,8 +120,8 @@ import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.r4.utils.ValidationProfileSet; import org.hl7.fhir.r4.utils.ValidationProfileSet;
import org.hl7.fhir.r4.utils.ValidationProfileSet.ProfileRegistration; 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.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -809,7 +809,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (ss) { if (ss) {
t = System.nanoTime(); t = System.nanoTime();
ValidationResult s = context.validateCode(new TerminologyServiceOptions("en"), system, code, display); ValidationResult s = context.validateCode(new TerminologyServiceOptions("en"), system, code, display);
txTime = txTime + (System.nanoTime() - t); txTime += (System.nanoTime() - t);
if (s == null) if (s == null)
return true; return true;
if (s.isOk()) { if (s.isOk()) {

View File

@ -296,6 +296,16 @@ public class EnableWhenEvaluator {
} }
retVal.addAll(findOnItem(item, question)); 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<Element> answers = extractAnswer(focus);
retVal.addAll(answers);
}
return retVal; return retVal;
} }

View File

@ -33,13 +33,11 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r5.model.Reference; import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.convertors.VersionConvertorConstants; import org.hl7.fhir.convertors.VersionConvertorConstants;
import org.hl7.fhir.convertors.VersionConvertor_10_50; import org.hl7.fhir.convertors.VersionConvertor_10_50;
import org.hl7.fhir.convertors.VersionConvertor_14_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.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r5.model.Range; import org.hl7.fhir.r5.model.Range;
import org.hl7.fhir.r5.model.Ratio; 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.Resource;
import org.hl7.fhir.r5.model.SampledData; import org.hl7.fhir.r5.model.SampledData;
import org.hl7.fhir.r5.model.StringType; 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.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 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.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; 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.ToolingExtensions;
import org.hl7.fhir.r5.utils.ValidationProfileSet; import org.hl7.fhir.r5.utils.ValidationProfileSet;
import org.hl7.fhir.r5.utils.ValidationProfileSet.ProfileRegistration; 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.r5.validation.EnableWhenEvaluator.QStack;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TerminologyServiceOptions;
@ -3170,7 +3165,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return null; return null;
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { private void validateQuestionnaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
String text = element.getNamedChildValue("text"); 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()); 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) { for (Element answer : answers) {
NodeStack ns = stack.push(answer, -1, null, null); NodeStack ns = stack.push(answer, -1, null, null);
switch (qItem.getType()) { if (qItem.getType() != null) {
case GROUP: switch (qItem.getType()) {
rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers"); case GROUP:
break; rule(errors, IssueType.STRUCTURE, answer.line(), answer.col(), stack.getLiteralPath(), false, "Items of type group should not have answers");
case DISPLAY: // nothing break;
break; case DISPLAY: // nothing
case BOOLEAN: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "boolean"); case BOOLEAN:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "boolean");
case DECIMAL: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "decimal"); case DECIMAL:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "decimal");
case INTEGER: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "integer"); case INTEGER:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "integer");
case DATE: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "date"); case DATE:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "date");
case DATETIME: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime"); case DATETIME:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "dateTime");
case TIME: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "time"); case TIME:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "time");
case STRING: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "string"); case STRING:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "string");
case TEXT: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "text"); case TEXT:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "text");
case URL: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "uri"); case URL:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "uri");
case ATTACHMENT: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment"); case ATTACHMENT:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "Attachment");
case REFERENCE: break;
validateQuestionnaireResponseItemType(errors, answer, ns, "Reference"); case REFERENCE:
break; validateQuestionnaireResponseItemType(errors, answer, ns, "Reference");
case QUANTITY: break;
if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity"))) case QUANTITY:
if (qItem.hasExtension("???")) if ("Quantity".equals(validateQuestionnaireResponseItemType(errors, answer, ns, "Quantity")))
validateQuestionnaireResponseItemQuantity(errors, answer, ns); if (qItem.hasExtension("???"))
break; validateQuestionnaireResponseItemQuantity(errors, answer, ns);
case CHOICE: break;
String itemType=validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); case CHOICE:
if (itemType != null) { String itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string");
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false); if (itemType != null) {
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, false);
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string"); 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: break;
itemType=validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string"); case OPENCHOICE:
if (itemType != null) { itemType = validateQuestionnaireResponseItemType(errors, answer, ns, "Coding", "date", "time", "integer", "string");
if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true); if (itemType != null) {
else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date"); if (itemType.equals("Coding")) validateAnswerCode(errors, answer, ns, qsrc, qItem, true);
else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time"); else if (itemType.equals("date")) checkOption(errors, answer, ns, qsrc, qItem, "date");
else if (itemType.equals("integer")) checkOption(errors, answer, ns, qsrc, qItem, "integer"); else if (itemType.equals("time")) checkOption(errors, answer, ns, qsrc, qItem, "time");
else if (itemType.equals("string")) checkOption(errors, answer, ns, qsrc, qItem, "string", true); 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: break;
case NULL: case QUESTION:
// no validation case NULL:
break; // no validation
break;
}
} }
validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack); 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; return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP;
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { private void validateQuestionnaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
if (elements.size() > 1) 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()); 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; int i = 0;
for (Element element : elements) { for (Element element : elements) {
NodeStack ns = stack.push(element, i, null, null); 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++; i++;
} }
} }
@ -3311,7 +3308,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (qItem != null) { if (qItem != null) {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem)); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
NodeStack ns = stack.push(item, -1, null, null); 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 else
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); 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"); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order");
lastIndex = index; lastIndex = index;
List<Element> mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>()); // 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
mapItem.add(item); if (item.hasChildren("answer") || item.hasChildren("item")) {
List<Element> 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) { for (QuestionnaireItemComponent qItem : qItems) {
List<Element> mapItem = map.get(qItem.getLinkId()); List<Element> 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<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List<Element> mapItem, QStack qstack) { public void validateQuestionnaireResponseItem(Questionnaire qsrc, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List<Element> mapItem, QStack qstack) {
boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(qItem, qstack); boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(qItem, qstack);
if (mapItem != null){ if (mapItem != null){
if (!enabled) { if (!enabled) {
@ -3346,13 +3346,24 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
i++; i++;
} }
} }
validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack);
} else { // Recursively validate child items
//item is missing, is the question enabled? validateQuestionnaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot, qstack);
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()+"'"); } 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) { private String misplacedItemError(QuestionnaireItemComponent qItem) {