mirror of
https://github.com/hapifhir/org.hl7.fhir.core.git
synced 2025-02-09 06:14:45 +00:00
Merge branch 'master' into do-20240131-cda-ccda-cli-versions
This commit is contained in:
commit
6b55cc8eb0
2
.gitignore
vendored
2
.gitignore
vendored
@ -307,3 +307,5 @@ local.properties
|
||||
/org.hl7.fhir.r4b.new
|
||||
|
||||
org.hl7.fhir.r5/var/lib/.fhir/packages/
|
||||
|
||||
org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/DebugUtilities.java
|
||||
|
@ -1,7 +1,33 @@
|
||||
## Validator Changes
|
||||
|
||||
* no changes
|
||||
* Fix significant bug where validator gets internal child lists incorrect and doesn't do complete validation when switching between validating profiles and resources directly
|
||||
* Performance improvement for validator parsing - thanks Brian Postlethwaite
|
||||
* Fix Crash on slicing a sliced element #862
|
||||
* Fix ig loading from direct URL (#1559)
|
||||
* Fix bug copying constraints into Bundle.entry.resource profiles
|
||||
* Fix bug loading R5 extensions with imported value sets
|
||||
* Replace dom-3 with custom java code, and check xhtml references to contained content
|
||||
* Improve concept map code validation
|
||||
* Update observation validator for committee decision not to enforce Blood Pressure profile for Mean Blood Pressure
|
||||
* Validate value set internal codeSystem references
|
||||
* Split value set validation into 10k batches for very large extensional value sets
|
||||
* Hack fix for opd-3
|
||||
* Fix bug where using ontoserver when not appropriate
|
||||
* Fix issues with inferSystem
|
||||
* Don't require HL7 committee for contained resources in HL7 namespace
|
||||
* Fix where validator was ignoring minimum cardinality for XML attributes (especially in CDA)
|
||||
* Improved ConceptMap validation
|
||||
|
||||
## Other code changes
|
||||
|
||||
* no changes
|
||||
* Fix code system rendering for uri properties
|
||||
* Fix broken links Bundle and Profile rendering
|
||||
* Take copy of code when doing local validation
|
||||
* WIP: major refactor of cross version analysis
|
||||
* Add support for subsumes in tx client
|
||||
* Don't generate snapshots when scanning structure definitions for resource names
|
||||
* Work on ConceptMap infrastructure for cross-version analysis
|
||||
* Fix bug where not rendering ConceptMap relationships
|
||||
* Fix wrong URLs rendering Profiles and Questionnaires
|
||||
* Fix bug using wrong version constant for R3
|
||||
* Updates for R5 StructureMap syntax
|
||||
|
@ -117,6 +117,13 @@ public class TerminologyClientR2 implements ITerminologyClient {
|
||||
p2 = client.operateType(org.hl7.fhir.dstu2.model.ValueSet.class, "validate-code", p2);
|
||||
return (Parameters) VersionConvertorFactory_10_50.convertResource(p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||
org.hl7.fhir.dstu2.model.Parameters p2 = (org.hl7.fhir.dstu2.model.Parameters) VersionConvertorFactory_10_50.convertResource(pin);
|
||||
p2 = client.operateType(org.hl7.fhir.dstu2.model.ValueSet.class, "subsumes", p2);
|
||||
return (Parameters) VersionConvertorFactory_10_50.convertResource(p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters validateVS(Parameters pin) throws FHIRException {
|
||||
@ -238,4 +245,5 @@ public class TerminologyClientR2 implements ITerminologyClient {
|
||||
org.hl7.fhir.dstu2.model.Bundle result = client.search(type, criteria);
|
||||
return result == null ? null : (Bundle) VersionConvertorFactory_10_50.convertResource(result);
|
||||
}
|
||||
|
||||
}
|
@ -242,4 +242,11 @@ public class TerminologyClientR3 implements ITerminologyClient {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||
org.hl7.fhir.dstu3.model.Parameters p2 = (org.hl7.fhir.dstu3.model.Parameters) VersionConvertorFactory_30_50.convertResource(pin);
|
||||
p2 = client.operateType(org.hl7.fhir.dstu3.model.CodeSystem.class, "subsumes", p2);
|
||||
return (Parameters) VersionConvertorFactory_30_50.convertResource(p2);
|
||||
}
|
||||
|
||||
}
|
@ -111,6 +111,25 @@ public class TerminologyClientR4 implements ITerminologyClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Parameters subsumes(Parameters pin) throws FHIRException {
|
||||
try {
|
||||
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
|
||||
p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "subsumes", p2);
|
||||
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
|
||||
} catch (EFhirClientException e) {
|
||||
if (e.getServerErrors().size() == 1) {
|
||||
OperationOutcome op = (OperationOutcome) VersionConvertorFactory_40_50.convertResource(e.getServerErrors().get(0));
|
||||
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), op, e);
|
||||
} else {
|
||||
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FHIRException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters validateVS(Parameters pin) throws FHIRException {
|
||||
try {
|
||||
@ -249,6 +268,5 @@ public class TerminologyClientR4 implements ITerminologyClient {
|
||||
org.hl7.fhir.r4.model.Bundle result = client.search(type, criteria);
|
||||
return result == null ? null : (Bundle) VersionConvertorFactory_40_50.convertResource(result);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -63,7 +63,7 @@ public class StructureMapUtilitiesTest implements ITransformerServices {
|
||||
@Test
|
||||
public void testSyntax() throws IOException, FHIRException {
|
||||
StructureMapUtilities scu = new StructureMapUtilities(context, this);
|
||||
String fileMap = TestingUtilities.loadTestResource("r5", "structure-mapping", "syntax.map");
|
||||
String fileMap = TestingUtilities.loadTestResource("r4b", "structure-mapping", "syntax.map");
|
||||
System.out.println(fileMap);
|
||||
|
||||
StructureMap structureMap = scu.parse(fileMap, "Syntax");
|
||||
|
@ -190,7 +190,10 @@ public class R5ExtensionsLoader {
|
||||
context.cacheResourceFromPackage(vs, vs.getSourcePackage());
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
for (CanonicalType t : inc.getValueSet()) {
|
||||
loadValueSet(t.asStringValue(), context, valueSets, codeSystems);
|
||||
ValueSet vsi = context.fetchResource(ValueSet.class, t.getValue());
|
||||
if (vsi == null) {
|
||||
loadValueSet(t.asStringValue(), context, valueSets, codeSystems);
|
||||
}
|
||||
}
|
||||
if (inc.hasSystem()) {
|
||||
if (!inc.hasVersion()) {
|
||||
|
@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComponent;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
@ -621,7 +622,12 @@ public class ProfilePathProcessor {
|
||||
if (firstTypeStructureDefinition.getSnapshot().getElement().isEmpty()) {
|
||||
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, firstTypeStructureDefinition.getVersionedUrl(), "Source for first element"));
|
||||
} else {
|
||||
src = firstTypeStructureDefinition.getSnapshot().getElement().get(0);
|
||||
src = firstTypeStructureDefinition.getSnapshot().getElement().get(0).copy();
|
||||
if (!src.getPath().contains(".") && firstTypeStructureDefinition.getKind() == StructureDefinitionKind.RESOURCE) {
|
||||
// we can't migrate the constraints in this case, because the sense of %resource changes when the root resource
|
||||
// is treated as an element. The validator will enforce the constraint
|
||||
src.getConstraint().clear(); //
|
||||
}
|
||||
}
|
||||
}
|
||||
template = src.copy().setPath(currentBase.getPath());
|
||||
@ -1063,7 +1069,7 @@ public class ProfilePathProcessor {
|
||||
}
|
||||
// throw new Error("Not done yet");
|
||||
// } else if (currentBase.getType().get(0).getCode().equals("BackboneElement") && diffMatches.size() > 0 && diffMatches.get(0).hasSliceName()) {
|
||||
} else if (currentBase.getType().get(0).getCode().equals("BackboneElement")) {
|
||||
} else if (!currentBase.getType().isEmpty() && currentBase.getType().get(0).getCode().equals("BackboneElement")) {
|
||||
// We need to copy children of the backbone element before we start messing around with slices
|
||||
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
|
||||
for (int i = cursors.baseCursor + 1; i <= newBaseLimit; i++) {
|
||||
|
@ -433,7 +433,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||
private boolean wantFixDifferentialFirstElementType;
|
||||
private Set<String> masterSourceFileNames;
|
||||
private Set<String> localFileNames;
|
||||
private Map<ElementDefinition, SourcedChildDefinitions> childMapCache = new HashMap<>();
|
||||
private Map<String, SourcedChildDefinitions> childMapCache = new HashMap<>();
|
||||
private AllowUnknownProfile allowUnknownProfile = AllowUnknownProfile.ALL_TYPES;
|
||||
private MappingMergeModeOption mappingMergeMode = MappingMergeModeOption.APPEND;
|
||||
private boolean forPublication;
|
||||
@ -479,8 +479,9 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||
}
|
||||
|
||||
public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
|
||||
if (childMapCache.containsKey(element)) {
|
||||
return childMapCache.get(element);
|
||||
String cacheKey = "cm."+profile.getVersionedUrl()+"#"+(element.hasId() ? element.getId() : element.getPath());
|
||||
if (childMapCache.containsKey(cacheKey)) {
|
||||
return childMapCache.get(cacheKey);
|
||||
}
|
||||
StructureDefinition src = profile;
|
||||
if (element.getContentReference() != null) {
|
||||
@ -524,7 +525,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||
break;
|
||||
}
|
||||
SourcedChildDefinitions result = new SourcedChildDefinitions(src, res);
|
||||
childMapCache.put(element, result);
|
||||
childMapCache.put(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1290,7 +1290,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
vsc.setUnknownSystems(unknownSystems);
|
||||
vsc.setThrowToServer(options.isUseServer() && terminologyClientManager.hasClient());
|
||||
if (!ValueSetUtilities.isServerSide(code.getSystem())) {
|
||||
res = vsc.validateCode(path, code);
|
||||
res = vsc.validateCode(path, code.copy());
|
||||
if (txCache != null && cachingAllowed) {
|
||||
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
|
||||
}
|
||||
@ -1347,7 +1347,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
Set<String> systems = findRelevantSystems(code, vs);
|
||||
TerminologyClientContext tc = terminologyClientManager.chooseServer(vs, systems, false);
|
||||
|
||||
String csumm =cachingAllowed && txCache != null ? txCache.summary(code) : null;
|
||||
String csumm = cachingAllowed && txCache != null ? txCache.summary(code) : null;
|
||||
if (cachingAllowed && txCache != null) {
|
||||
txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress());
|
||||
} else {
|
||||
@ -1377,6 +1377,83 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ask the terminology system whether parent subsumes child.
|
||||
*
|
||||
* @return true if it does, false if it doesn't, and null if it's not know whether it does
|
||||
*/
|
||||
public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) {
|
||||
ValidationOptions options = optionsArg != null ? optionsArg : ValidationOptions.defaults();
|
||||
|
||||
if (parent.hasSystem()) {
|
||||
codeSystemsUsed.add(parent.getSystem());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (child.hasSystem()) {
|
||||
codeSystemsUsed.add(child.getSystem());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CacheToken cacheToken = cachingAllowed && txCache != null ? txCache.generateSubsumesToken(options, parent, child, expParameters) : null;
|
||||
if (cachingAllowed && txCache != null) {
|
||||
Boolean res = txCache.getSubsumes(cacheToken);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.isUseClient() && parent.getSystem().equals(child.getSystem())) {
|
||||
CodeSystem cs = fetchCodeSystem(parent.getSystem());
|
||||
if (cs != null) {
|
||||
Boolean b = CodeSystemUtilities.subsumes(cs, parent.getCode(), child.getCode());
|
||||
if (txCache != null && cachingAllowed) {
|
||||
txCache.cacheSubsumes(cacheToken, b, true);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
if (!terminologyClientManager.hasClient() || !options.isUseServer() || unsupportedCodeSystems.contains(parent.getSystem()) || unsupportedCodeSystems.contains(child.getSystem()) || noTerminologyServer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<String> systems = new HashSet<>();
|
||||
systems.add(parent.getSystem());
|
||||
systems.add(child.getSystem());
|
||||
TerminologyClientContext tc = terminologyClientManager.chooseServer(null, systems, false);
|
||||
|
||||
txLog("$subsumes "+parent.toString()+" > "+child.toString()+" on "+tc.getAddress());
|
||||
|
||||
try {
|
||||
Parameters pIn = new Parameters();
|
||||
pIn.addParameter().setName("codingA").setValue(parent);
|
||||
pIn.addParameter().setName("codingB").setValue(child);
|
||||
if (txLog != null) {
|
||||
txLog.clearLastId();
|
||||
}
|
||||
Parameters pOut = tc.getClient().subsumes(pIn);
|
||||
return processSubsumesResult(pOut, tc.getClient().getAddress());
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public Boolean processSubsumesResult(Parameters pOut, String server) {
|
||||
for (ParametersParameterComponent p : pOut.getParameter()) {
|
||||
if (p.hasValue()) {
|
||||
if (p.getName().equals("outcome")) {
|
||||
return Utilities.existsInList(p.getValue().primitiveValue(), "equivalent", "subsumes");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
|
||||
return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging());
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||
private List<StructureDefinition> allStructuresList = new ArrayList<StructureDefinition>();
|
||||
private List<String> canonicalResourceNames;
|
||||
private List<String> concreteResourceNames;
|
||||
private Set<String> concreteResourceNameSet;
|
||||
|
||||
public ContextUtilities(IWorkerContext context) {
|
||||
super();
|
||||
@ -317,6 +318,9 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||
|
||||
@Override
|
||||
public boolean isResource(String t) {
|
||||
if (getConcreteResourceSet().contains(t)) {
|
||||
return true;
|
||||
}
|
||||
StructureDefinition sd;
|
||||
try {
|
||||
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t);
|
||||
@ -370,16 +374,21 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getConcreteResources() {
|
||||
if (concreteResourceNames == null) {
|
||||
concreteResourceNames = new ArrayList<>();
|
||||
Set<String> names = new HashSet<>();
|
||||
for (StructureDefinition sd : allStructures()) {
|
||||
if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract()) {
|
||||
names.add(sd.getType());
|
||||
public Set<String> getConcreteResourceSet() {
|
||||
if (concreteResourceNameSet == null) {
|
||||
concreteResourceNameSet = new HashSet<>();
|
||||
for (StructureDefinition sd : getStructures()) {
|
||||
if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract() && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
concreteResourceNameSet.add(sd.getType());
|
||||
}
|
||||
}
|
||||
concreteResourceNames.addAll(Utilities.sorted(names));
|
||||
}
|
||||
return concreteResourceNameSet;
|
||||
}
|
||||
|
||||
public List<String> getConcreteResources() {
|
||||
if (concreteResourceNames == null) {
|
||||
concreteResourceNames.addAll(Utilities.sorted(concreteResourceNameSet));
|
||||
}
|
||||
return concreteResourceNames;
|
||||
}
|
||||
|
@ -640,4 +640,11 @@ public interface IWorkerContext {
|
||||
public <T extends Resource> T findTxResource(Class<T> class_, String canonical);
|
||||
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version);
|
||||
|
||||
/**
|
||||
* ask the terminology system whether parent subsumes child.
|
||||
*
|
||||
* @return true if it does, false if it doesn't, and null if it's not know whether it does
|
||||
*/
|
||||
public Boolean subsumes(ValidationOptions options, Coding parent, Coding child);
|
||||
|
||||
}
|
@ -514,7 +514,7 @@ public class Element extends Base implements NamedItem {
|
||||
}
|
||||
|
||||
private boolean isDataType(Base v) {
|
||||
return v instanceof DataType && new ContextUtilities(property.getContext()).getTypeNames().contains(v.fhirType());
|
||||
return v instanceof DataType && property.getContextUtils().getTypeNames().contains(v.fhirType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,7 +134,7 @@ public class FmlParser extends ParserBase {
|
||||
lexer.token("conceptmap");
|
||||
Element map = structureMap.makeElement("contained");
|
||||
StructureDefinition sd = context.fetchTypeDefinition("ConceptMap");
|
||||
map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(map.getElementProperty() != null ? map.getElementProperty() : map.getProperty()), map.getProperty());
|
||||
map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(map.getElementProperty() != null ? map.getElementProperty() : map.getProperty()), map.getProperty());
|
||||
map.setType("ConceptMap");
|
||||
Element eid = map.makeElement("id").markLocation(lexer.getCurrentLocation());
|
||||
String id = lexer.readConstant("map id");
|
||||
@ -225,6 +225,8 @@ public class FmlParser extends ParserBase {
|
||||
String token = lexer.take();
|
||||
if (token.equals("-"))
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
if (token.equals("=")) // temporary
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
if (token.equals("=="))
|
||||
return ConceptMapRelationship.EQUIVALENT;
|
||||
if (token.equals("!="))
|
||||
|
@ -86,22 +86,15 @@ public class JsonParser extends ParserBase {
|
||||
private JsonCreator json;
|
||||
private boolean allowComments;
|
||||
|
||||
private ProfileUtilities profileUtilities;
|
||||
private Element baseElement;
|
||||
private ContextUtilities contextUtilities;
|
||||
|
||||
public JsonParser(IWorkerContext context, ProfileUtilities utilities) {
|
||||
super(context);
|
||||
super(context, utilities);
|
||||
|
||||
this.profileUtilities = utilities;
|
||||
contextUtilities = new ContextUtilities(context);
|
||||
}
|
||||
|
||||
public JsonParser(IWorkerContext context) {
|
||||
super(context);
|
||||
|
||||
this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context));
|
||||
contextUtilities = new ContextUtilities(context);
|
||||
}
|
||||
|
||||
public Element parse(String source, String type) throws Exception {
|
||||
@ -128,7 +121,7 @@ public class JsonParser extends ParserBase {
|
||||
nEd.addType().setCode(type);
|
||||
nEd.setMax(obj.getProperties().get(0).getValue().isJsonArray() ? "*" : "1");
|
||||
}
|
||||
Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)).setFormat(FhirFormat.JSON);
|
||||
Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON);
|
||||
result.setPath(type);
|
||||
checkObject(focusFragment.getErrors(), obj, result, path);
|
||||
result.setType(type);
|
||||
@ -199,7 +192,7 @@ public class JsonParser extends ParserBase {
|
||||
name = sd.getType();
|
||||
path = sd.getTypeTail();
|
||||
}
|
||||
baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)).setFormat(FhirFormat.JSON);
|
||||
baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON);
|
||||
checkObject(errors, object, baseElement, path);
|
||||
baseElement.markLocation(line(object), col(object));
|
||||
baseElement.setType(name);
|
||||
@ -259,9 +252,9 @@ public class JsonParser extends ParserBase {
|
||||
if (policy != ValidationPolicy.NONE) {
|
||||
for (JsonProperty e : children) {
|
||||
if (e.getTag() == 0) {
|
||||
StructureDefinition sd = element.getProperty().isLogical() ? contextUtilities.fetchByJsonName(e.getName()) : null;
|
||||
StructureDefinition sd = element.getProperty().isLogical() ? getContextUtilities().fetchByJsonName(e.getName()) : null;
|
||||
if (sd != null) {
|
||||
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils());
|
||||
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils(), element.getProperty().getContextUtils());
|
||||
parseChildItem(errors, path, children, element, property);
|
||||
} else if ("fhir_comments".equals(e.getName()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
|
||||
if (!e.getValue().isJsonArray()) {
|
||||
@ -341,7 +334,7 @@ public class JsonParser extends ParserBase {
|
||||
if (type == null) {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(je), property.getName(), property.typeSummary()), IssueSeverity.ERROR);
|
||||
} else if (property.hasType(type)) {
|
||||
Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), type);
|
||||
Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), property.getContextUtils(), type);
|
||||
parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, np, path, property.getName(), false);
|
||||
} else {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(je), property.getName(), type, property.typeSummary()), IssueSeverity.ERROR);
|
||||
@ -474,7 +467,7 @@ public class JsonParser extends ParserBase {
|
||||
if (type == null) {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(pv.getValue()), propV.getName(), propV.typeSummary()), IssueSeverity.ERROR);
|
||||
} else if (propV.hasType(type)) {
|
||||
pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), type);
|
||||
pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), propV.getContextUtils(), type);
|
||||
ok = true;
|
||||
} else {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(pv.getValue()), propV.getName(), type, propV.typeSummary()), IssueSeverity.ERROR);
|
||||
@ -713,7 +706,7 @@ public class JsonParser extends ParserBase {
|
||||
if (sd == null) {
|
||||
logError(errors, ValidationMessage.NO_RULE_DATE, line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL);
|
||||
} else {
|
||||
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
|
||||
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
|
||||
parent.setType(name);
|
||||
parseChildren(errors, npath, res, parent, true, null);
|
||||
}
|
||||
|
@ -39,8 +39,10 @@ import java.util.List;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
|
||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
@ -89,14 +91,26 @@ public abstract class ParserBase {
|
||||
protected IdRenderingPolicy idPolicy = IdRenderingPolicy.All;
|
||||
protected StructureDefinition logical;
|
||||
protected IDigitalSignatureServices signatureServices;
|
||||
|
||||
public ParserBase(IWorkerContext context) {
|
||||
private ProfileUtilities profileUtilities;
|
||||
private ContextUtilities contextUtilities;
|
||||
|
||||
public ParserBase(IWorkerContext context, ProfileUtilities utilities) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.profileUtilities = utilities;
|
||||
contextUtilities = new ContextUtilities(context);
|
||||
policy = ValidationPolicy.NONE;
|
||||
}
|
||||
|
||||
public void setupValidation(ValidationPolicy policy) {
|
||||
public ParserBase(IWorkerContext context) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.profileUtilities = new ProfileUtilities(context, null, null, new FHIRPathEngine(context));
|
||||
contextUtilities = new ContextUtilities(context);
|
||||
policy = ValidationPolicy.NONE;
|
||||
}
|
||||
|
||||
public void setupValidation(ValidationPolicy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@ -215,19 +229,19 @@ public abstract class ParserBase {
|
||||
// first pass: only look at base definitions
|
||||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
||||
if (sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/"+name)) {
|
||||
new ContextUtilities(context).generateSnapshot(sd);
|
||||
contextUtilities.generateSnapshot(sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
||||
if (name.equals(sd.getTypeName()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
new ContextUtilities(context).generateSnapshot(sd);
|
||||
contextUtilities.generateSnapshot(sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
|
||||
if (name.equals(sd.getUrl()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
new ContextUtilities(context).generateSnapshot(sd);
|
||||
contextUtilities.generateSnapshot(sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
@ -304,4 +318,22 @@ public abstract class ParserBase {
|
||||
return element.getNamedChildValue("reference");
|
||||
}
|
||||
}
|
||||
|
||||
public IWorkerContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public ValidationPolicy getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public ProfileUtilities getProfileUtilities() {
|
||||
return profileUtilities;
|
||||
}
|
||||
|
||||
public ContextUtilities getContextUtilities() {
|
||||
return contextUtilities;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -38,6 +38,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefinitions;
|
||||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.fhirpath.TypeDetails;
|
||||
import org.hl7.fhir.r5.formats.FormatUtilities;
|
||||
@ -60,21 +61,24 @@ public class Property {
|
||||
private ElementDefinition definition;
|
||||
private StructureDefinition structure;
|
||||
private ProfileUtilities profileUtilities;
|
||||
private ContextUtilities utils;
|
||||
private TypeRefComponent type;
|
||||
|
||||
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities) {
|
||||
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils) {
|
||||
this.context = context;
|
||||
this.definition = definition;
|
||||
this.structure = structure;
|
||||
this.utils = utils;
|
||||
this.profileUtilities = profileUtilities;
|
||||
}
|
||||
|
||||
|
||||
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, String type) {
|
||||
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils, String type) {
|
||||
this.context = context;
|
||||
this.definition = definition;
|
||||
this.structure = structure;
|
||||
this.profileUtilities = profileUtilities;
|
||||
this.utils = utils;
|
||||
for (TypeRefComponent tr : definition.getType()) {
|
||||
if (tr.getWorkingCode().equals(type)) {
|
||||
this.type = tr;
|
||||
@ -83,7 +87,7 @@ public class Property {
|
||||
}
|
||||
|
||||
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) {
|
||||
this(context, definition, structure, new ProfileUtilities(context, null, null));
|
||||
this(context, definition, structure, new ProfileUtilities(context, null, null), new ContextUtilities(context));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -265,10 +269,10 @@ public class Property {
|
||||
public boolean isResource() {
|
||||
if (type != null) {
|
||||
String tc = type.getCode();
|
||||
return (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames()));
|
||||
return (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc));
|
||||
} else if (definition.getType().size() > 0) {
|
||||
String tc = definition.getType().get(0).getCode();
|
||||
return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames()));
|
||||
return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc));
|
||||
}
|
||||
else {
|
||||
return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE);
|
||||
@ -425,7 +429,7 @@ public class Property {
|
||||
}
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
for (ElementDefinition child : children.getList()) {
|
||||
properties.add(new Property(context, child, sd, this.profileUtilities));
|
||||
properties.add(new Property(context, child, sd, this.profileUtilities, this.utils));
|
||||
}
|
||||
profileUtilities.getCachedPropertyList().put(cacheKey, properties);
|
||||
return properties;
|
||||
@ -485,7 +489,7 @@ public class Property {
|
||||
}
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
for (ElementDefinition child : children.getList()) {
|
||||
properties.add(new Property(context, child, sd, this.profileUtilities));
|
||||
properties.add(new Property(context, child, sd, this.profileUtilities, this.utils));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
@ -613,6 +617,9 @@ public class Property {
|
||||
public ProfileUtilities getUtils() {
|
||||
return profileUtilities;
|
||||
}
|
||||
public ContextUtilities getContextUtils() {
|
||||
return utils;
|
||||
}
|
||||
|
||||
public boolean isJsonPrimitiveChoice() {
|
||||
return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE);
|
||||
|
@ -50,7 +50,7 @@ public class ResourceParser extends ParserBase {
|
||||
if (sd == null) {
|
||||
throw new FHIRException("No definition exists for "+resource.fhirType());
|
||||
}
|
||||
Property p = new Property(context, sd.getSnapshot().getElement().get(0), sd, new ProfileUtilities(context, null, null));
|
||||
Property p = new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities());
|
||||
String path = resource.fhirType();
|
||||
|
||||
Element e = new Element(resource.fhirType(), p);
|
||||
@ -106,7 +106,7 @@ public class ResourceParser extends ParserBase {
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(v.fhirType(), null));
|
||||
if (sd == null)
|
||||
throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, v.fhirType()));
|
||||
n.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), p);
|
||||
n.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(n.getProperty()), p);
|
||||
n.setType(v.fhirType());
|
||||
parseChildren(npath, v, n);
|
||||
} else {
|
||||
|
@ -151,7 +151,7 @@ public class TurtleParser extends ParserBase {
|
||||
if (sd == null)
|
||||
return null;
|
||||
|
||||
Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.TURTLE);
|
||||
Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.TURTLE);
|
||||
result.markLocation(cmp.getLine(), cmp.getCol());
|
||||
result.setType(name);
|
||||
parseChildren(errors, src, path, cmp, result, false);
|
||||
@ -279,7 +279,7 @@ public class TurtleParser extends ParserBase {
|
||||
|
||||
Element n = new Element(tail(name), property).markLocation(object.getLine(), object.getCol()).setFormat(FhirFormat.TURTLE);
|
||||
element.getChildren().add(n);
|
||||
n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), property);
|
||||
n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(n.getProperty()), property);
|
||||
n.setType(rt);
|
||||
parseChildren(errors, src, npath, obj, n, false);
|
||||
}
|
||||
|
@ -456,7 +456,7 @@ public class VerticalBarParser extends ParserBase {
|
||||
@Override
|
||||
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message");
|
||||
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd));
|
||||
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd, getProfileUtilities(), getContextUtilities()));
|
||||
byte[] content = TextFile.streamToBytes(inStream);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(content);
|
||||
VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset);
|
||||
|
@ -231,7 +231,7 @@ public class XmlParser extends ParserBase {
|
||||
if (sd == null)
|
||||
return null;
|
||||
|
||||
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
|
||||
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML);
|
||||
result.setPath(element.getLocalName());
|
||||
checkElement(errors, element, result, path, result.getProperty(), false);
|
||||
result.markLocation(line(element, false), col(element, false));
|
||||
@ -321,7 +321,7 @@ public class XmlParser extends ParserBase {
|
||||
|
||||
public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element base, String type) throws Exception {
|
||||
StructureDefinition sd = getDefinition(errors, 0, 0, FormatUtilities.FHIR_NS, type);
|
||||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML).setNativeObject(base);
|
||||
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML).setNativeObject(base);
|
||||
result.setPath(base.getLocalName());
|
||||
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
|
||||
checkElement(errors, base, result, path, result.getProperty(), false);
|
||||
@ -662,7 +662,7 @@ public class XmlParser extends ParserBase {
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
|
||||
if (sd == null)
|
||||
throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, res.getLocalName()));
|
||||
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
|
||||
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
|
||||
parent.setType(name);
|
||||
parseChildren(errors, res.getLocalName(), res, parent);
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ public abstract class FormatUtilities {
|
||||
}
|
||||
|
||||
public static boolean isValidId(String tail) {
|
||||
return tail.matches(ID_REGEX);
|
||||
return tail == null ? false : tail.matches(ID_REGEX);
|
||||
}
|
||||
|
||||
public static String makeId(String candidate) {
|
||||
|
@ -596,6 +596,9 @@ public abstract class CanonicalResource extends DomainResource {
|
||||
}
|
||||
|
||||
public String present() {
|
||||
if (hasUserData("presentation")) {
|
||||
return getUserString("presentation");
|
||||
}
|
||||
if (hasTitle())
|
||||
return getTitle();
|
||||
if (hasName())
|
||||
|
@ -1777,10 +1777,23 @@ public class ConceptMap extends MetadataResource {
|
||||
|
||||
}
|
||||
|
||||
public SourceElementComponent getOrAddElement(String code) {
|
||||
for (SourceElementComponent e : getElement()) {
|
||||
if (code.equals(e.getCode())) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return addElement().setCode(code);
|
||||
}
|
||||
}
|
||||
|
||||
@Block()
|
||||
public static class SourceElementComponent extends BackboneElement implements IBaseBackboneElement {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SourceElementComponent [code=" + code + ", display=" + display + ", noMap=" + noMap + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity (code or path) or the element/item being mapped.
|
||||
*/
|
||||
@ -2264,6 +2277,11 @@ public class ConceptMap extends MetadataResource {
|
||||
|
||||
@Block()
|
||||
public static class TargetElementComponent extends BackboneElement implements IBaseBackboneElement {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TargetElementComponent [code=" + code + ", relationship=" + relationship + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity (code or path) or the element/item that the map refers to.
|
||||
*/
|
||||
|
@ -4605,6 +4605,11 @@ public boolean hasTarget() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + ":" + expression + (severity == null ? "("+severity.asStringValue()+")" : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Block()
|
||||
@ -13093,6 +13098,10 @@ If a pattern[x] is declared on a repeating element, the pattern applies to all r
|
||||
return t;
|
||||
}
|
||||
|
||||
public boolean repeats() {
|
||||
return !Utilities.existsInList(getMax(), "0", "1");
|
||||
}
|
||||
|
||||
// end addition
|
||||
|
||||
}
|
||||
|
@ -3829,6 +3829,17 @@ public class Enumerations {
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
public String getSymbol() {
|
||||
switch (this) {
|
||||
case RELATEDTO: return "-";
|
||||
case EQUIVALENT: return "=";
|
||||
case SOURCEISNARROWERTHANTARGET: return "<";
|
||||
case SOURCEISBROADERTHANTARGET: return ">";
|
||||
case NOTRELATEDTO: return "!=";
|
||||
case NULL: return null;
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConceptMapRelationshipEnumFactory implements EnumFactory<ConceptMapRelationship> {
|
||||
|
@ -114,15 +114,19 @@ public class ExtensionHelper {
|
||||
* @return The extension, if on this element, else null. will check modifier extensions too
|
||||
*/
|
||||
public static Extension getExtension(BackboneElement element, String name) {
|
||||
if (name == null || element == null || !element.hasExtension())
|
||||
if (name == null || element == null)
|
||||
return null;
|
||||
for (Extension e : element.getModifierExtension()) {
|
||||
if (name.equals(e.getUrl()))
|
||||
return e;
|
||||
if (element.hasModifierExtension()) {
|
||||
for (Extension e : element.getModifierExtension()) {
|
||||
if (name.equals(e.getUrl()))
|
||||
return e;
|
||||
}
|
||||
}
|
||||
for (Extension e : element.getExtension()) {
|
||||
if (name.equals(e.getUrl()))
|
||||
return e;
|
||||
if (element.hasExtension()) {
|
||||
for (Extension e : element.getExtension()) {
|
||||
if (name.equals(e.getUrl()))
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1499,6 +1499,34 @@ public class StructureDefinition extends CanonicalResource {
|
||||
|
||||
}
|
||||
|
||||
//added from java-adornments.txt:
|
||||
|
||||
public ElementDefinition getElementByPath(String path) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
for (ElementDefinition ed : getElement()) {
|
||||
if (path.equals(ed.getPath()) || (path+"[x]").equals(ed.getPath())) {
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public ElementDefinition getElementById(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
for (ElementDefinition ed : getElement()) {
|
||||
if (id.equals(ed.getId())) {
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//end addition
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1150,6 +1150,15 @@ public class ValueSet extends MetadataResource {
|
||||
|
||||
}
|
||||
|
||||
public boolean hasConcept(String code) {
|
||||
for (ConceptReferenceComponent c : getConcept()) {
|
||||
if (code.equals(c.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Block()
|
||||
|
@ -11,6 +11,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
|
||||
@ -539,8 +540,15 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||
if (first) first = false; else td.addText(", ");
|
||||
if (pcv.hasValueCoding()) {
|
||||
td.addText(pcv.getValueCoding().getCode());
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) {
|
||||
CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, pcv.getValue().primitiveValue());
|
||||
if (cr != null) {
|
||||
td.ah(cr.getWebPath(), cr.getVersionedUrl()).tx(cr.present());
|
||||
} else if (Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||
} else {
|
||||
td.code(pcv.getValue().primitiveValue());
|
||||
}
|
||||
} else if ("parent".equals(pcv.getCode())) {
|
||||
td.ah("#"+cs.getId()+"-"+Utilities.nmtokenize(pcv.getValue().primitiveValue())).addText(pcv.getValue().primitiveValue());
|
||||
} else {
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.hl7.fhir.r5.renderers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -9,8 +12,10 @@ import java.util.Map;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupUnmappedMode;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.MappingPropertyComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.OtherElementComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||
@ -22,11 +27,254 @@ import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
public class ConceptMapRenderer extends TerminologyRenderer {
|
||||
|
||||
public enum RenderMultiRowSortPolicy {
|
||||
UNSORTED, FIRST_COL, LAST_COL
|
||||
|
||||
}
|
||||
|
||||
public interface IConceptMapInformationProvider {
|
||||
public List<Coding> getMembers(String uri);
|
||||
public String getLink(String system, String code);
|
||||
}
|
||||
|
||||
public static class MultipleMappingRowSorter implements Comparator<MultipleMappingRow> {
|
||||
|
||||
private boolean first;
|
||||
|
||||
protected MultipleMappingRowSorter(boolean first) {
|
||||
super();
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(MultipleMappingRow o1, MultipleMappingRow o2) {
|
||||
String s1 = first ? o1.firstCode() : o1.lastCode();
|
||||
String s2 = first ? o2.firstCode() : o2.lastCode();
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cell {
|
||||
|
||||
private String system;
|
||||
private String code;
|
||||
private String display;
|
||||
private String relationship;
|
||||
private String relComment;
|
||||
public boolean renderedRel;
|
||||
public boolean renderedCode;
|
||||
private Cell clone;
|
||||
|
||||
protected Cell() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Cell(String system, String code, String display) {
|
||||
this.system = system;
|
||||
this.code = code;
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
public Cell(String system, String code, String relationship, String comment) {
|
||||
this.system = system;
|
||||
this.code = code;
|
||||
this.relationship = relationship;
|
||||
this.relComment = comment;
|
||||
}
|
||||
|
||||
public boolean matches(String system, String code) {
|
||||
return (system != null && system.equals(this.system)) && (code != null && code.equals(this.code));
|
||||
}
|
||||
|
||||
public String present() {
|
||||
if (system == null) {
|
||||
return code;
|
||||
} else {
|
||||
return code; //+(clone == null ? "" : " (@"+clone.code+")");
|
||||
}
|
||||
}
|
||||
|
||||
public Cell copy(boolean clone) {
|
||||
Cell res = new Cell();
|
||||
res.system = system;
|
||||
res.code = code;
|
||||
res.display = display;
|
||||
res.relationship = relationship;
|
||||
res.relComment = relComment;
|
||||
res.renderedRel = renderedRel;
|
||||
res.renderedCode = renderedCode;
|
||||
if (clone) {
|
||||
res.clone = this;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return relationship+" "+system + "#" + code + " \"" + display + "\"";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class MultipleMappingRowItem {
|
||||
List<Cell> cells = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (Cell cell : cells) {
|
||||
if (cell.relationship != null) {
|
||||
b.append(cell.relationship+cell.code);
|
||||
} else {
|
||||
b.append(cell.code);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultipleMappingRow {
|
||||
private List<MultipleMappingRowItem> rowSets = new ArrayList<>();
|
||||
private MultipleMappingRow stickySource;
|
||||
|
||||
public MultipleMappingRow(int i, String system, String code, String display) {
|
||||
MultipleMappingRowItem row = new MultipleMappingRowItem();
|
||||
rowSets.add(row);
|
||||
for (int c = 0; c < i; c++) {
|
||||
row.cells.add(new Cell()); // blank cell spaces
|
||||
}
|
||||
row.cells.add(new Cell(system, code, display));
|
||||
}
|
||||
|
||||
|
||||
public MultipleMappingRow(MultipleMappingRow stickySource) {
|
||||
this.stickySource = stickySource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (MultipleMappingRowItem rowSet : rowSets) {
|
||||
b.append(""+rowSet.cells.size());
|
||||
}
|
||||
CommaSeparatedStringBuilder b2 = new CommaSeparatedStringBuilder(";");
|
||||
for (MultipleMappingRowItem rowSet : rowSets) {
|
||||
b2.append(rowSet.toString());
|
||||
}
|
||||
return ""+rowSets.size()+" ["+b.toString()+"] ("+b2.toString()+")";
|
||||
}
|
||||
|
||||
|
||||
public String lastCode() {
|
||||
MultipleMappingRowItem first = rowSets.get(0);
|
||||
for (int i = first.cells.size()-1; i >= 0; i--) {
|
||||
if (first.cells.get(i).code != null) {
|
||||
return first.cells.get(i).code;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String firstCode() {
|
||||
MultipleMappingRowItem first = rowSets.get(0);
|
||||
for (int i = 0; i < first.cells.size(); i++) {
|
||||
if (first.cells.get(i).code != null) {
|
||||
return first.cells.get(i).code;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void addSource(MultipleMappingRow sourceRow, List<MultipleMappingRow> rowList, ConceptMapRelationship relationship, String comment) {
|
||||
// we already have a row, and we're going to collapse the rows on sourceRow into here, and add a matching terminus
|
||||
assert sourceRow.rowSets.get(0).cells.size() == rowSets.get(0).cells.size()-1;
|
||||
rowList.remove(sourceRow);
|
||||
Cell template = rowSets.get(0).cells.get(rowSets.get(0).cells.size()-1);
|
||||
for (MultipleMappingRowItem row : sourceRow.rowSets) {
|
||||
row.cells.add(new Cell(template.system, template.code, relationship.getSymbol(), comment));
|
||||
}
|
||||
rowSets.addAll(sourceRow.rowSets);
|
||||
}
|
||||
|
||||
public void addTerminus() {
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
row.cells.add(new Cell(null, null, "X", null));
|
||||
}
|
||||
}
|
||||
|
||||
public void addTarget(String system, String code, ConceptMapRelationship relationship, String comment, List<MultipleMappingRow> sets, int colCount) {
|
||||
if (rowSets.get(0).cells.size() == colCount+1) { // if it's already has a target for this col then we have to clone (and split) the rows
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
row.cells.add(new Cell(system, code, relationship.getSymbol(), comment));
|
||||
}
|
||||
} else {
|
||||
MultipleMappingRow nrow = new MultipleMappingRow(this);
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
MultipleMappingRowItem n = new MultipleMappingRowItem();
|
||||
for (int i = 0; i < row.cells.size()-1; i++) { // note to skip the last
|
||||
n.cells.add(row.cells.get(i).copy(true));
|
||||
}
|
||||
n.cells.add(new Cell(system, code, relationship.getSymbol(), comment));
|
||||
nrow.rowSets.add(n);
|
||||
}
|
||||
sets.add(sets.indexOf(this), nrow);
|
||||
}
|
||||
}
|
||||
|
||||
public String lastSystem() {
|
||||
MultipleMappingRowItem first = rowSets.get(0);
|
||||
for (int i = first.cells.size()-1; i >= 0; i--) {
|
||||
if (first.cells.get(i).system != null) {
|
||||
return first.cells.get(i).system;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void addCopy(String system) {
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
row.cells.add(new Cell(system, lastCode(), "=", null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean alreadyHasMappings(int i) {
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
if (row.cells.size() > i+1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public Cell getLastSource(int i) {
|
||||
for (MultipleMappingRowItem row : rowSets) {
|
||||
return row.cells.get(i+1);
|
||||
}
|
||||
throw new Error("Should not get here"); // return null
|
||||
}
|
||||
|
||||
|
||||
public void cloneSource(int i, Cell cell) {
|
||||
MultipleMappingRowItem row = new MultipleMappingRowItem();
|
||||
rowSets.add(row);
|
||||
for (int c = 0; c < i-1; c++) {
|
||||
row.cells.add(new Cell()); // blank cell spaces
|
||||
}
|
||||
row.cells.add(cell.copy(true));
|
||||
row.cells.add(rowSets.get(0).cells.get(rowSets.get(0).cells.size()-1).copy(false));
|
||||
}
|
||||
}
|
||||
|
||||
public ConceptMapRenderer(RenderingContext context) {
|
||||
super(context);
|
||||
}
|
||||
@ -475,4 +723,254 @@ public class ConceptMapRenderer extends TerminologyRenderer {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static XhtmlNode renderMultipleMaps(String start, List<ConceptMap> maps, IConceptMapInformationProvider linker, RenderMultiRowSortPolicy sort) {
|
||||
// 1+1 column for each provided map
|
||||
List<MultipleMappingRow> rowSets = new ArrayList<>();
|
||||
for (int i = 0; i < maps.size(); i++) {
|
||||
populateRows(rowSets, maps.get(i), i, linker);
|
||||
}
|
||||
collateRows(rowSets);
|
||||
if (sort != RenderMultiRowSortPolicy.UNSORTED) {
|
||||
Collections.sort(rowSets, new MultipleMappingRowSorter(sort == RenderMultiRowSortPolicy.FIRST_COL));
|
||||
}
|
||||
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
|
||||
XhtmlNode tbl = div.table("none").style("text-align: left; border-spacing: 0; padding: 5px");
|
||||
XhtmlNode tr = tbl.tr();
|
||||
styleCell(tr.td(), false, true, 5).b().tx(start);
|
||||
for (ConceptMap map : maps) {
|
||||
if (map.hasWebPath()) {
|
||||
styleCell(tr.td(), false, true, 5).colspan(2).b().ah(map.getWebPath(), map.getVersionedUrl()).tx(map.present());
|
||||
} else {
|
||||
styleCell(tr.td(), false, true, 5).colspan(2).b().tx(map.present());
|
||||
}
|
||||
}
|
||||
for (MultipleMappingRow row : rowSets) {
|
||||
renderMultiRow(tbl, row, maps, linker);
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
private static void collateRows(List<MultipleMappingRow> rowSets) {
|
||||
List<MultipleMappingRow> toDelete = new ArrayList<ConceptMapRenderer.MultipleMappingRow>();
|
||||
for (MultipleMappingRow rowSet : rowSets) {
|
||||
MultipleMappingRow tgt = rowSet.stickySource;
|
||||
while (toDelete.contains(tgt)) {
|
||||
tgt = tgt.stickySource;
|
||||
}
|
||||
if (tgt != null && rowSets.contains(tgt)) {
|
||||
tgt.rowSets.addAll(rowSet.rowSets);
|
||||
toDelete.add(rowSet);
|
||||
}
|
||||
}
|
||||
rowSets.removeAll(toDelete);
|
||||
}
|
||||
|
||||
private static void renderMultiRow(XhtmlNode tbl, MultipleMappingRow rows, List<ConceptMap> maps, IConceptMapInformationProvider linker) {
|
||||
int rowCounter = 0;
|
||||
for (MultipleMappingRowItem row : rows.rowSets) {
|
||||
XhtmlNode tr = tbl.tr();
|
||||
boolean first = true;
|
||||
int cellCounter = 0;
|
||||
Cell last = null;
|
||||
for (Cell cell : row.cells) {
|
||||
if (first) {
|
||||
if (!cell.renderedCode) {
|
||||
int c = 1;
|
||||
for (int i = rowCounter + 1; i < rows.rowSets.size(); i++) {
|
||||
if (cell.code != null && rows.rowSets.get(i).cells.size() > cellCounter && cell.code.equals(rows.rowSets.get(i).cells.get(cellCounter).code)) {
|
||||
rows.rowSets.get(i).cells.get(cellCounter).renderedCode = true;
|
||||
c++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cell.code == null) {
|
||||
styleCell(tr.td(), rowCounter == 0, true, 5).rowspan(c).style("background-color: #eeeeee");
|
||||
} else {
|
||||
String link = linker.getLink(cell.system, cell.code);
|
||||
XhtmlNode x = null;
|
||||
if (link != null) {
|
||||
x = styleCell(tr.td(), rowCounter == 0, true, 5).attributeNN("title", cell.display).rowspan(c).ah(link);
|
||||
} else {
|
||||
x = styleCell(tr.td(), rowCounter == 0, true, 5).attributeNN("title", cell.display).rowspan(c);
|
||||
}
|
||||
// if (cell.clone != null) {
|
||||
// x.style("color: grey");
|
||||
// }
|
||||
x.tx(cell.present());
|
||||
}
|
||||
}
|
||||
first = false;
|
||||
} else {
|
||||
if (!cell.renderedRel) {
|
||||
int c = 1;
|
||||
for (int i = rowCounter + 1; i < rows.rowSets.size(); i++) {
|
||||
if ((cell.relationship != null && rows.rowSets.get(i).cells.size() > cellCounter && cell.relationship.equals(rows.rowSets.get(i).cells.get(cellCounter).relationship)) &&
|
||||
(cell.code != null && cell.code.equals(rows.rowSets.get(i).cells.get(cellCounter).code)) &&
|
||||
(last.code != null && cell.code.equals(rows.rowSets.get(i).cells.get(cellCounter-1).code))) {
|
||||
rows.rowSets.get(i).cells.get(cellCounter).renderedRel = true;
|
||||
c++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last.code == null || cell.code == null) {
|
||||
styleCell(tr.td(), rowCounter == 0, true, 5).style("background-color: #eeeeee");
|
||||
} else if (cell.relationship != null) {
|
||||
styleCell(tr.tdW(16), rowCounter == 0, true, 0).attributeNN("title", cell.relComment).rowspan(c).style("background-color: LightGrey; text-align: center; vertical-align: middle; color: white").tx(cell.relationship);
|
||||
} else {
|
||||
styleCell(tr.tdW(16), rowCounter == 0, false, 0).rowspan(c);
|
||||
}
|
||||
}
|
||||
if (!cell.renderedCode) {
|
||||
int c = 1;
|
||||
for (int i = rowCounter + 1; i < rows.rowSets.size(); i++) {
|
||||
if (cell.code != null && rows.rowSets.get(i).cells.size() > cellCounter && cell.code.equals(rows.rowSets.get(i).cells.get(cellCounter).code)) {
|
||||
rows.rowSets.get(i).cells.get(cellCounter).renderedCode = true;
|
||||
c++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cell.code == null) {
|
||||
styleCell(tr.td(), rowCounter == 0, true, 5).rowspan(c).style("background-color: #eeeeee");
|
||||
} else {
|
||||
String link = linker.getLink(cell.system, cell.code);
|
||||
XhtmlNode x = null;
|
||||
if (link != null) {
|
||||
x = styleCell(tr.td(), rowCounter == 0, true, 5).attributeNN("title", cell.display).rowspan(c).ah(link);
|
||||
} else {
|
||||
x = styleCell(tr.td(), rowCounter == 0, true, 5).attributeNN("title", cell.display).rowspan(c);
|
||||
}
|
||||
// if (cell.clone != null) {
|
||||
// x.style("color: grey");
|
||||
// }
|
||||
x.tx(cell.present());
|
||||
}
|
||||
}
|
||||
}
|
||||
last = cell;
|
||||
cellCounter++;
|
||||
}
|
||||
rowCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
private static XhtmlNode styleCell(XhtmlNode td, boolean firstrow, boolean sides, int padding) {
|
||||
if (firstrow) {
|
||||
td.style("vertical-align: middle; border-top: 1px solid black; padding: "+padding+"px");
|
||||
} else {
|
||||
td.style("vertical-align: middle; border-top: 1px solid LightGrey; padding: "+padding+"px");
|
||||
}
|
||||
if (sides) {
|
||||
td.style("border-left: 1px solid LightGrey; border-right: 2px solid LightGrey");
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
private static void populateRows(List<MultipleMappingRow> rowSets, ConceptMap map, int i, IConceptMapInformationProvider linker) {
|
||||
// if we can resolve the value set, we create entries for it
|
||||
if (map.hasSourceScope()) {
|
||||
List<Coding> codings = linker.getMembers(map.getSourceScope().primitiveValue());
|
||||
if (codings != null) {
|
||||
for (Coding c : codings) {
|
||||
MultipleMappingRow row = i == 0 ? null : findExistingRowBySource(rowSets, c.getSystem(), c.getCode(), i);
|
||||
if (row == null) {
|
||||
row = new MultipleMappingRow(i, c.getSystem(), c.getCode(), c.getDisplay());
|
||||
rowSets.add(row);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ConceptMapGroupComponent grp : map.getGroup()) {
|
||||
for (SourceElementComponent src : grp.getElement()) {
|
||||
MultipleMappingRow row = findExistingRowBySource(rowSets, grp.getSource(), src.getCode(), i);
|
||||
if (row == null) {
|
||||
row = new MultipleMappingRow(i, grp.getSource(), src.getCode(), src.getDisplay());
|
||||
rowSets.add(row);
|
||||
}
|
||||
if (src.getNoMap()) {
|
||||
row.addTerminus();
|
||||
} else {
|
||||
List<TargetElementComponent> todo = new ArrayList<>();
|
||||
for (TargetElementComponent tgt : src.getTarget()) {
|
||||
MultipleMappingRow trow = findExistingRowByTarget(rowSets, grp.getTarget(), tgt.getCode(), i);
|
||||
if (trow == null) {
|
||||
row.addTarget(grp.getTarget(), tgt.getCode(), tgt.getRelationship(), tgt.getComment(), rowSets, i);
|
||||
} else {
|
||||
todo.add(tgt);
|
||||
}
|
||||
}
|
||||
// we've already got a mapping to these targets. So we gather them under the one mapping - but do this after the others are done
|
||||
for (TargetElementComponent t : todo) {
|
||||
MultipleMappingRow trow = findExistingRowByTarget(rowSets, grp.getTarget(), t.getCode(), i);
|
||||
if (row.alreadyHasMappings(i)) {
|
||||
// src is already mapped, and so is target, and now we need to map src to target too
|
||||
// we have to clone src, but we only clone the last
|
||||
trow.cloneSource(i, row.getLastSource(i));
|
||||
} else {
|
||||
trow.addSource(row, rowSets, t.getRelationship(), t.getComment());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean copy = grp.hasUnmapped() && grp.getUnmapped().getMode() == ConceptMapGroupUnmappedMode.USESOURCECODE;
|
||||
if (copy) {
|
||||
for (MultipleMappingRow row : rowSets) {
|
||||
if (row.rowSets.get(0).cells.size() == i && row.lastSystem().equals(grp.getSource())) {
|
||||
row.addCopy(grp.getTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MultipleMappingRow row : rowSets) {
|
||||
if (row.rowSets.get(0).cells.size() == i) {
|
||||
row.addTerminus();
|
||||
}
|
||||
}
|
||||
if (map.hasTargetScope()) {
|
||||
List<Coding> codings = linker.getMembers(map.getTargetScope().primitiveValue());
|
||||
if (codings != null) {
|
||||
for (Coding c : codings) {
|
||||
MultipleMappingRow row = findExistingRowByTarget(rowSets, c.getSystem(), c.getCode(), i);
|
||||
if (row == null) {
|
||||
row = new MultipleMappingRow(i+1, c.getSystem(), c.getCode(), c.getDisplay());
|
||||
rowSets.add(row);
|
||||
} else {
|
||||
for (MultipleMappingRowItem cells : row.rowSets) {
|
||||
Cell last = cells.cells.get(cells.cells.size() -1);
|
||||
if (last.system != null && last.system.equals(c.getSystem()) && last.code.equals(c.getCode()) && last.display == null) {
|
||||
last.display = c.getDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static MultipleMappingRow findExistingRowByTarget(List<MultipleMappingRow> rows, String system, String code, int i) {
|
||||
for (MultipleMappingRow row : rows) {
|
||||
for (MultipleMappingRowItem cells : row.rowSets) {
|
||||
if (cells.cells.size() > i + 1 && cells.cells.get(i+1).matches(system, code)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MultipleMappingRow findExistingRowBySource(List<MultipleMappingRow> rows, String system, String code, int i) {
|
||||
for (MultipleMappingRow row : rows) {
|
||||
for (MultipleMappingRowItem cells : row.rowSets) {
|
||||
if (cells.cells.size() > i && cells.cells.get(i).matches(system, code)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
@ -224,14 +225,15 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
return Utilities.pathURL(context.getLink(KnownLinkType.SPEC), path);
|
||||
}
|
||||
|
||||
private String getSDCLink(String path) {
|
||||
if (Utilities.isAbsoluteUrl(path)) {
|
||||
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, path);
|
||||
if (sd != null) {
|
||||
return sd.getWebPath();
|
||||
} else {
|
||||
return path.replace("StructureDefinition/", "StructureDefinition-")+".html";
|
||||
}
|
||||
private String getSDCLink(String url, String path) {
|
||||
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url);
|
||||
if (sd == null) {
|
||||
sd = context.getContext().fetchResource(StructureDefinition.class, path);
|
||||
}
|
||||
if (sd != null && sd.hasWebPath()) {
|
||||
return sd.getWebPath();
|
||||
} else if (Utilities.isAbsoluteUrl(path)) {
|
||||
return path.replace("StructureDefinition/", "StructureDefinition-")+".html";
|
||||
} else {
|
||||
return Utilities.pathURL("http://hl7.org/fhir/uv/sdc", path); // for now?
|
||||
}
|
||||
@ -264,16 +266,16 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-readonly.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-subject.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-subject.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_HIDDEN)) {
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-hidden.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_OTP_DISP)) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-optional.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-optional.png"))));
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-observation.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", "StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-observation.png"))));
|
||||
}
|
||||
if (i.hasExtension(ToolingExtensions.EXT_Q_CHOICE_ORIENT)) {
|
||||
String code = ToolingExtensions.readStringExtension(i, ToolingExtensions.EXT_Q_CHOICE_ORIENT);
|
||||
@ -447,7 +449,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
private void addExpression(Piece p, Expression exp, String label, String url) {
|
||||
XhtmlNode x = new XhtmlNode(NodeType.Element, "li").style("font-size: 11px");
|
||||
p.addHtml(x);
|
||||
x.ah(url).tx(label);
|
||||
CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, url);
|
||||
if (cr != null && cr.hasWebPath()) {
|
||||
x.ah(cr.getWebPath()).tx(label);
|
||||
} else {
|
||||
x.ah(url).tx(label);
|
||||
}
|
||||
x.tx(": ");
|
||||
x.code(exp.getExpression());
|
||||
}
|
||||
@ -712,7 +719,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject"), "Can change the subject of the questionnaire").img(getImgPath("icon-qi-subject.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject", "StructureDefinition-sdc-questionnaire-isSubject.html"), "Can change the subject of the questionnaire").img(getImgPath("icon-qi-subject.png"), "icon");
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_HIDDEN)) {
|
||||
hasFlag = true;
|
||||
@ -721,11 +728,11 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_OTP_DISP)) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink(ToolingExtensions.EXT_Q_OTP_DISP), "Is optional to display").img(getImgPath("icon-qi-optional.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay", "StructureDefinition-sdc-questionnaire-optionalDisplay.html"), "Is optional to display").img(getImgPath("icon-qi-optional.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod"), "Is linked to an observation").img(getImgPath("icon-qi-observation.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod", "StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), "Is linked to an observation").img(getImgPath("icon-qi-observation.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) {
|
||||
CodeableConcept cc = i.getExtensionByUrl(ToolingExtensions.EXT_Q_DISPLAY_CAT).getValueCodeableConcept();
|
||||
@ -1031,7 +1038,12 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
}
|
||||
if (qi.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
XhtmlNode tr = tbl.tr();
|
||||
tr.td().ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod").tx("Observation Link Period");
|
||||
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, ToolingExtensions.EXT_O_LINK_PERIOD);
|
||||
if (sd != null && sd.hasWebPath()) {
|
||||
tr.td().ah(sd.getWebPath()).tx("Observation Link Period");
|
||||
} else {
|
||||
tr.td().ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod").tx("Observation Link Period");
|
||||
}
|
||||
render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod").getValue());
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,12 @@ public abstract class ResourceRenderer extends DataRenderer {
|
||||
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||
boolean hasExtensions;
|
||||
hasExtensions = render(x, r);
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
inject(r, x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
|
||||
}
|
||||
|
||||
@ -99,12 +105,53 @@ public abstract class ResourceRenderer extends DataRenderer {
|
||||
assert r.getContext() == context;
|
||||
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||
boolean hasExtensions = render(x, r);
|
||||
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
if (r.hasNarrative()) {
|
||||
r.injectNarrative(x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode checkNarrative(ResourceWrapper r) throws IOException, FHIRException, EOperationOutcome {
|
||||
assert r.getContext() == context;
|
||||
XhtmlNode x = r.getNarrative();
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private void injectAnchorName(XhtmlNode x, String an) {
|
||||
XhtmlNode ip = x;
|
||||
while (ip.hasChildren() && "div".equals(ip.getChildNodes().get(0).getName())) {
|
||||
ip = ip.getChildNodes().get(0);
|
||||
}
|
||||
ip.addTag(0, "a").setAttribute("name", an).tx(" ");
|
||||
}
|
||||
|
||||
protected boolean hasAnchorName(XhtmlNode x, String an) {
|
||||
if ("a".equals(x.getName()) && an.equals(x.getAttribute("name"))) {
|
||||
return true;
|
||||
}
|
||||
if (x.hasChildren()) {
|
||||
for (XhtmlNode c : x.getChildNodes()) {
|
||||
if (hasAnchorName(c, an)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome;
|
||||
|
||||
public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
|
||||
@ -438,7 +485,11 @@ public abstract class ResourceRenderer extends DataRenderer {
|
||||
String bundleUrl = null;
|
||||
Element br = bundleElement.getNamedChild("resource", false);
|
||||
if (br.getChildValue("id") != null) {
|
||||
bundleUrl = "#" + br.fhirType() + "_" + br.getChildValue("id");
|
||||
if ("Bundle".equals(br.fhirType())) {
|
||||
bundleUrl = "#";
|
||||
} else {
|
||||
bundleUrl = "#" + br.fhirType() + "_" + br.getChildValue("id");
|
||||
}
|
||||
} else {
|
||||
bundleUrl = "#" +fullUrlToAnchor(bundleElement.getChildValue("fullUrl"));
|
||||
}
|
||||
|
@ -792,8 +792,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
UnusedTracker used = new UnusedTracker();
|
||||
String ref = defPath == null ? null : defPath + anchorPrefix + element.getId();
|
||||
String sName = tail(element.getPath());
|
||||
if (element.hasSliceName())
|
||||
if (element.hasSliceName()) {
|
||||
sName = sName +":"+element.getSliceName();
|
||||
}
|
||||
used.used = true;
|
||||
if (logicalModel) {
|
||||
if (element.hasRepresentation(PropertyRepresentation.XMLATTR)) {
|
||||
@ -1331,10 +1332,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
String ref2 = null;
|
||||
String fixedUrl = null;
|
||||
if (ed != null) {
|
||||
String p = ed.getWebPath();
|
||||
if (p != null) {
|
||||
ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p);
|
||||
}
|
||||
String p = ed.getWebPath();
|
||||
fixedUrl = getFixedUrl(ed);
|
||||
if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension?
|
||||
if (fixedUrl.equals(url))
|
||||
@ -1583,7 +1581,8 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
abr.render(gen, c);
|
||||
}
|
||||
for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
|
||||
if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
|
||||
// if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
|
||||
if (!inv.hasSource() || profile == null || allInvariants || (!isAbstractBaseProfile(inv.getSource()) && !"http://hl7.org/fhir/StructureDefinition/Extension".equals(inv.getSource()))) {
|
||||
if (!c.getPieces().isEmpty())
|
||||
c.addPiece(gen.new Piece("br"));
|
||||
c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold")));
|
||||
@ -1670,6 +1669,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
return c;
|
||||
}
|
||||
|
||||
private boolean isAbstractBaseProfile(String source) {
|
||||
StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, source);
|
||||
return (sd != null) && sd.getAbstract() && sd.hasUrl() && sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/");
|
||||
}
|
||||
|
||||
private Piece checkAddExternalFlag(BindingResolution br, Piece piece) {
|
||||
if (br.external) {
|
||||
piece.setTagImg("external.png");
|
||||
@ -2566,15 +2570,22 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
}
|
||||
|
||||
public String listConstraintsAndConditions(ElementDefinition element) {
|
||||
Set<String> ids = new HashSet<>();
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (ElementDefinitionConstraintComponent con : element.getConstraint()) {
|
||||
if (!isBaseConstraint(con)) {
|
||||
b.append(con.getKey());
|
||||
if (!ids.contains(con.getKey())) {
|
||||
ids.add(con.getKey());
|
||||
b.append(con.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IdType id : element.getCondition()) {
|
||||
if (!isBaseCondition(id)) {
|
||||
b.append(id.asStringValue());
|
||||
if (!ids.contains(id.asStringValue())) {
|
||||
ids.add(id.asStringValue());
|
||||
b.append(id.asStringValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
@ -3315,6 +3326,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
if (!tl.contains(tc)) {
|
||||
aliases.add(name.replace("[x]", Utilities.capitalize(tc)));
|
||||
aliases.add(name+":"+name.replace("[x]", Utilities.capitalize(tc)));
|
||||
aliases.add(name.replace("[x]", Utilities.capitalize(tc))+":"+name.replace("[x]", Utilities.capitalize(tc)));
|
||||
tl.add(tc);
|
||||
}
|
||||
}
|
||||
@ -3334,7 +3346,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
||||
list.addAll(generated);
|
||||
}
|
||||
ElementDefinition ed = stack.get(stack.size()-1);
|
||||
|
||||
// now we have all the possible names, but some of them might be inappropriate if we've
|
||||
// already generated a type slicer. On the other hand, if we've already done that, we're
|
||||
// going to steal any type specific ones off it.
|
||||
|
@ -82,6 +82,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||
|
||||
private static final int MAX_DESIGNATIONS_IN_LINE = 5;
|
||||
|
||||
private static final int MAX_BATCH_VALIDATION_SIZE = 1000;
|
||||
|
||||
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
|
||||
|
||||
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
|
||||
@ -1431,12 +1433,23 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||
}
|
||||
}
|
||||
if (!context.isNoSlowLookup() && !serverList.isEmpty()) {
|
||||
getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), serverList, null);
|
||||
for (CodingValidationRequest vr : serverList) {
|
||||
ConceptDefinitionComponent v = vr.getResult().asConceptDefinition();
|
||||
if (v != null) {
|
||||
results.put(vr.getCoding().getCode(), v);
|
||||
try {
|
||||
// todo: split this into 10k batches
|
||||
int i = 0;
|
||||
while (serverList.size() > i) {
|
||||
int len = Integer.min(serverList.size(), MAX_BATCH_VALIDATION_SIZE);
|
||||
List<CodingValidationRequest> list = serverList.subList(i, i+len);
|
||||
i += len;
|
||||
getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), list, null);
|
||||
for (CodingValidationRequest vr : list) {
|
||||
ConceptDefinitionComponent v = vr.getResult().asConceptDefinition();
|
||||
if (v != null) {
|
||||
results.put(vr.getCoding().getCode(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
@ -215,6 +215,8 @@ public class RenderingContext {
|
||||
|
||||
private Map<KnownLinkType, String> links = new HashMap<>();
|
||||
private Map<String, String> namedLinks = new HashMap<>();
|
||||
private boolean addName = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context - access to all related resources that might be needed
|
||||
@ -728,5 +730,14 @@ public class RenderingContext {
|
||||
this.fixedFormat = fixedFormat;
|
||||
}
|
||||
|
||||
public boolean isAddName() {
|
||||
return addName;
|
||||
}
|
||||
|
||||
public RenderingContext setAddName(boolean addName) {
|
||||
this.addName = addName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -33,6 +33,7 @@ package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
@ -206,6 +207,11 @@ public class CodeSystemUtilities extends TerminologyUtilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isNotSelectable(CodeSystem cs, String code) {
|
||||
ConceptDefinitionComponent cd = findCode(cs.getConcept(), code);
|
||||
return cd == null ? false : isNotSelectable(cs, cd);
|
||||
}
|
||||
|
||||
public static void setNotSelectable(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError {
|
||||
defineNotSelectableProperty(cs);
|
||||
ConceptPropertyComponent p = getProperty(concept, "notSelectable");
|
||||
@ -476,6 +482,28 @@ public class CodeSystemUtilities extends TerminologyUtilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static List<ConceptDefinitionComponent> findCodeWithParents(List<ConceptDefinitionComponent> parents, List<ConceptDefinitionComponent> list, String code) {
|
||||
for (ConceptDefinitionComponent c : list) {
|
||||
if (c.hasCode() && c.getCode().equals(code)) {
|
||||
return addToList(parents, c);
|
||||
}
|
||||
List<ConceptDefinitionComponent> s = findCodeWithParents(addToList(parents, c), c.getConcept(), code);
|
||||
if (s != null)
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<ConceptDefinitionComponent> addToList(List<ConceptDefinitionComponent> parents, ConceptDefinitionComponent c) {
|
||||
List<ConceptDefinitionComponent> res = new ArrayList<CodeSystem.ConceptDefinitionComponent>();
|
||||
if (parents != null) {
|
||||
res.addAll(parents);
|
||||
}
|
||||
res.add(c);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ConceptDefinitionComponent findCodeOrAltCode(List<ConceptDefinitionComponent> list, String code, String use) {
|
||||
for (ConceptDefinitionComponent c : list) {
|
||||
if (c.hasCode() && c.getCode().equals(code))
|
||||
@ -928,5 +956,35 @@ public class CodeSystemUtilities extends TerminologyUtilities {
|
||||
return v.primitiveValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean subsumes(CodeSystem cs, String pc, String cc) {
|
||||
if (pc.equals(cc)) {
|
||||
return true;
|
||||
}
|
||||
List<ConceptDefinitionComponent> child = findCodeWithParents(null, cs.getConcept(), cc);
|
||||
for (ConceptDefinitionComponent item : child) {
|
||||
if (pc.equals(item.getCode())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Set<String> codes(CodeSystem cs) {
|
||||
Set<String> res = new HashSet<>();
|
||||
addCodes(res, cs.getConcept());
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void addCodes(Set<String> res, List<ConceptDefinitionComponent> list) {
|
||||
for (ConceptDefinitionComponent cd : list) {
|
||||
if (cd.hasCode()) {
|
||||
res.add(cd.getCode());
|
||||
}
|
||||
if (cd.hasConcept()) {
|
||||
addCodes(res, cd.getConcept());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,13 @@
|
||||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
@ -10,6 +15,9 @@ import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ConceptMapElementSorter;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ElementMappingPair;
|
||||
import org.hl7.fhir.r5.model.Identifier;
|
||||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
@ -17,6 +25,70 @@ import org.hl7.fhir.r5.model.ValueSet;
|
||||
|
||||
public class ConceptMapUtilities {
|
||||
|
||||
public static class TargetSorter implements Comparator<TargetElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(TargetElementComponent o1, TargetElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ElementSorter implements Comparator<SourceElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(SourceElementComponent o1, SourceElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ElementMappingPair {
|
||||
|
||||
private SourceElementComponent src;
|
||||
private TargetElementComponent tgt;
|
||||
|
||||
public ElementMappingPair(SourceElementComponent src, TargetElementComponent tgt) {
|
||||
this.src = src;
|
||||
this.tgt = tgt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TranslatedCode {
|
||||
private String code;
|
||||
private ConceptMapRelationship relationship;
|
||||
public TranslatedCode(String code, ConceptMapRelationship relationship) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.relationship = relationship;
|
||||
}
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
public ConceptMapRelationship getRelationship() {
|
||||
return relationship;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConceptMapElementSorter implements Comparator<SourceElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(SourceElementComponent o1, SourceElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConceptMapTargetElementSorter implements Comparator<TargetElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(TargetElementComponent o1, TargetElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
public static boolean hasOID(ConceptMap cm) {
|
||||
return getOID(cm) != null;
|
||||
}
|
||||
@ -85,4 +157,377 @@ public class ConceptMapUtilities {
|
||||
return cm;
|
||||
}
|
||||
|
||||
public static ConceptMap invert(ConceptMap src, String id, String url, String name, boolean collate) {
|
||||
ConceptMap dst = src.copy();
|
||||
dst.setId(id);
|
||||
dst.setUrl(url);
|
||||
dst.setName(name);
|
||||
dst.getGroup().clear();
|
||||
dst.setSourceScope(src.getTargetScope());
|
||||
dst.setTargetScope(src.getSourceScope());
|
||||
for (ConceptMapGroupComponent gs : src.getGroup()) {
|
||||
ConceptMapGroupComponent gd = dst.addGroup();
|
||||
gd.setTargetElement(gs.getSourceElement());
|
||||
gd.setSourceElement(gs.getTargetElement());
|
||||
Map<String, SourceElementComponent> dstMap = new HashMap<>();
|
||||
for (SourceElementComponent es : gs.getElement()) {
|
||||
for (TargetElementComponent ts : es.getTarget()) {
|
||||
SourceElementComponent ed = collate ? dstMap.get(ts.getCode()) : null;
|
||||
if (ed == null) {
|
||||
ed = gd.addElement();
|
||||
ed.setCodeElement(ts.getCodeElement());
|
||||
if (collate) {
|
||||
dstMap.put(ed.getCode(), ed);
|
||||
}
|
||||
}
|
||||
TargetElementComponent td = ed.addTarget();
|
||||
td.setCode(es.getCode());
|
||||
td.setComment(ts.getComment());
|
||||
td.setRelationship(invertRelationship(ts.getRelationship()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static ConceptMapRelationship invertRelationship(ConceptMapRelationship relationship) {
|
||||
if (relationship == null) {
|
||||
return null;
|
||||
}
|
||||
switch (relationship) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.EQUIVALENT;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return ConceptMapRelationship.NULL;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConceptMap collapse(String id, String url, boolean cumulative, ConceptMap src, ConceptMap... sequence) {
|
||||
ConceptMap res = src.copy();
|
||||
res.setId(id);
|
||||
res.setUrl(url);
|
||||
|
||||
for (ConceptMap cm : sequence) {
|
||||
if (res.hasTargetScope() && src.hasTargetScope()) {
|
||||
if (!cm.getSourceScope().equals(cm.getTargetScope())) {
|
||||
throw new Error("Mismatch between seqeuntial concept maps: ");
|
||||
} else {
|
||||
res.setTargetScope(cm.getTargetScope());
|
||||
}
|
||||
} else {
|
||||
res.setTargetScope(null);
|
||||
}
|
||||
}
|
||||
|
||||
for (ConceptMapGroupComponent gd : res.getGroup()) {
|
||||
for (ConceptMap cm : sequence) {
|
||||
for (ConceptMapGroupComponent gt : cm.getGroup()) {
|
||||
if (gt.getSource().equals(gd.getTarget())) {
|
||||
gd.setTarget(gt.getTarget());
|
||||
|
||||
List<SourceElementComponent> processed = new ArrayList<ConceptMap.SourceElementComponent>();
|
||||
for (SourceElementComponent ed : gd.getElement()) {
|
||||
List<TargetElementComponent> list = new ArrayList<>();
|
||||
list.addAll(ed.getTarget());
|
||||
ed.getTarget().clear();
|
||||
for (TargetElementComponent ts : list) {
|
||||
for (SourceElementComponent et : gt.getElement()) {
|
||||
if (et.getCode().equals(ed.getCode())) {
|
||||
processed.add(et);
|
||||
for (TargetElementComponent tt : et.getTarget()) {
|
||||
ed.addTarget().setCode(tt.getCode()).setRelationship(combineRelationships(ts.getRelationship(), tt.getRelationship()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ed.getTarget().isEmpty()) {
|
||||
if (cumulative) {
|
||||
ed.getTarget().addAll(list);
|
||||
} else {
|
||||
ed.setNoMap(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cumulative) {
|
||||
for (SourceElementComponent et : gt.getElement()) {
|
||||
if (!processed.contains(et)) {
|
||||
gd.addElement(et.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(gt.getElement(), new ConceptMapElementSorter());
|
||||
for (SourceElementComponent e: gt.getElement()) {
|
||||
Collections.sort(e.getTarget(), new ConceptMapTargetElementSorter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ConceptMapRelationship combineRelationships(ConceptMapRelationship rel1, ConceptMapRelationship rel2) {
|
||||
switch (rel1) {
|
||||
case EQUIVALENT:
|
||||
return rel2;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return rel2;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
switch (rel2) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
}
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
switch (rel2) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean checkReciprocal(ConceptMap left, ConceptMap right, List<String> issues) {
|
||||
boolean altered = false;
|
||||
if (!Base.compareDeep(left.getTargetScope(), right.getSourceScope(), true)) {
|
||||
issues.add("scopes are not reciprocal: "+left.getTargetScope()+" vs "+right.getSourceScope());
|
||||
}
|
||||
if (!Base.compareDeep(left.getSourceScope(), right.getTargetScope(), true)) {
|
||||
issues.add("scopes are not reciprocal: "+left.getSourceScope()+" vs "+right.getTargetScope());
|
||||
}
|
||||
if (left.getGroup().size() != right.getGroup().size()) {
|
||||
issues.add("group count mismatch: "+left.getGroup().size()+" vs "+right.getGroup().size());
|
||||
}
|
||||
for (ConceptMapGroupComponent gl : left.getGroup()) {
|
||||
ConceptMapGroupComponent gr = findMatchingGroup(right.getGroup(), gl.getTarget(), gl.getSource());
|
||||
if (gr == null) {
|
||||
issues.add("left maps from "+gl.getSource()+" to "+gl.getTarget()+" but right has no matching reverse map");
|
||||
} else {
|
||||
for (SourceElementComponent srcL : gl.getElement()) {
|
||||
if (!"CHECK!".equals(srcL.getCode())) {
|
||||
if (!srcL.getNoMap()) {
|
||||
for (TargetElementComponent tgtL : srcL.getTarget()) {
|
||||
List<ElementMappingPair> pairs = getMappings(gr, tgtL.getCode(), srcL.getCode());
|
||||
switch (tgtL.getRelationship()) {
|
||||
case EQUIVALENT:
|
||||
if (pairs.isEmpty()) {
|
||||
gr.getOrAddElement(tgtL.getCode()).addTarget().setCode(srcL.getCode()).setRelationship(ConceptMapRelationship.EQUIVALENT);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) {
|
||||
issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RELATEDTO:
|
||||
if (pairs.isEmpty()) {
|
||||
gr.getOrAddElement(tgtL.getCode()).addTarget().setCode(srcL.getCode()).setRelationship(ConceptMapRelationship.RELATEDTO);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) {
|
||||
issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
if (pairs.isEmpty()) {
|
||||
gr.getOrAddElement(tgtL.getCode()).addTarget().setCode(srcL.getCode()).setRelationship(ConceptMapRelationship.SOURCEISNARROWERTHANTARGET);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
|
||||
issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
if (pairs.isEmpty()) {
|
||||
gr.getOrAddElement(tgtL.getCode()).addTarget().setCode(srcL.getCode()).setRelationship(ConceptMapRelationship.SOURCEISBROADERTHANTARGET);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) {
|
||||
issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NOTRELATEDTO:
|
||||
for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
|
||||
issues.add("Left map says that "+srcL.getCode()+" is not related to "+tgtL.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SourceElementComponent srcR : gr.getElement()) {
|
||||
for (TargetElementComponent tgtR : srcR.getTarget()) {
|
||||
if (srcL.getCode().equals(tgtR.getCode())) {
|
||||
issues.add("Left map says that there is no relationship for "+srcL.getCode()+" but right map has a "+tgtR.getRelationship().toCode()+" mapping to it from "+srcR.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SourceElementComponent srcR : gr.getElement()) {
|
||||
if (!"CHECK!".equals(srcR.getCode())) {
|
||||
if (!srcR.getNoMap()) {
|
||||
for (TargetElementComponent tgtR : srcR.getTarget()) {
|
||||
List<ElementMappingPair> pairs = getMappings(gl, tgtR.getCode(), srcR.getCode());
|
||||
switch (tgtR.getRelationship()) {
|
||||
case EQUIVALENT:
|
||||
if (pairs.isEmpty()) {
|
||||
gl.getOrAddElement(tgtR.getCode()).addTarget().setCode(srcR.getCode()).setRelationship(ConceptMapRelationship.EQUIVALENT);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) {
|
||||
issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RELATEDTO:
|
||||
if (pairs.isEmpty()) {
|
||||
gl.getOrAddElement(tgtR.getCode()).addTarget().setCode(srcR.getCode()).setRelationship(ConceptMapRelationship.RELATEDTO);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) {
|
||||
issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
if (pairs.isEmpty()) {
|
||||
gl.getOrAddElement(tgtR.getCode()).addTarget().setCode(srcR.getCode()).setRelationship(ConceptMapRelationship.SOURCEISNARROWERTHANTARGET);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) {
|
||||
issues.add("Right map says that "+srcR.getCode()+" is broader than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
if (pairs.isEmpty()) {
|
||||
gl.getOrAddElement(tgtR.getCode()).addTarget().setCode(srcR.getCode()).setRelationship(ConceptMapRelationship.SOURCEISBROADERTHANTARGET);
|
||||
altered = true;
|
||||
} else for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) {
|
||||
issues.add("Right map says that "+srcR.getCode()+" is narrower than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NOTRELATEDTO:
|
||||
for (ElementMappingPair pair : pairs) {
|
||||
if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) {
|
||||
issues.add("Right map says that "+srcR.getCode()+" is not related to "+tgtR.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SourceElementComponent srcL : gr.getElement()) {
|
||||
for (TargetElementComponent tgtL : srcL.getTarget()) {
|
||||
if (srcR.getCode().equals(tgtL.getCode())) {
|
||||
issues.add("Right map says that there is no relationship for "+srcR.getCode()+" but right map has a "+tgtL.getRelationship().toCode()+" mapping to it from "+srcL.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return altered;
|
||||
}
|
||||
|
||||
private static List<ElementMappingPair> getMappings(ConceptMapGroupComponent g, String source, String target) {
|
||||
List<ElementMappingPair> res = new ArrayList<ConceptMapUtilities.ElementMappingPair>();
|
||||
|
||||
for (SourceElementComponent src : g.getElement()) {
|
||||
for (TargetElementComponent tgt : src.getTarget()) {
|
||||
if (source.equals(src.getCode()) && target.equals(tgt.getCode())) {
|
||||
res.add(new ElementMappingPair(src, tgt));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static ConceptMapGroupComponent findMatchingGroup(List<ConceptMapGroupComponent> groups, String source, String target) {
|
||||
for (ConceptMapGroupComponent g : groups) {
|
||||
if (source.equals(g.getSource()) && target.equals(g.getTarget())) {
|
||||
return g;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cmF
|
||||
* @return true if all the maps simply map to the same code
|
||||
*/
|
||||
public static boolean isUnityMap(ConceptMap cm) {
|
||||
for (ConceptMapGroupComponent grp : cm.getGroup()) {
|
||||
for (SourceElementComponent src : grp.getElement()) {
|
||||
if (src.hasNoMap()) {
|
||||
return false;
|
||||
}
|
||||
if (src.getTarget().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
if (src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.EQUIVALENT && src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.RELATEDTO) {
|
||||
return false;
|
||||
}
|
||||
if (!src.getCode().equals(src.getTargetFirstRep().getCode())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int mapCount(ConceptMap cm) {
|
||||
int i = 0;
|
||||
for (ConceptMapGroupComponent grp : cm.getGroup()) {
|
||||
for (SourceElementComponent src : grp.getElement()) {
|
||||
i = i + src.getTarget().size();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -479,4 +479,25 @@ public class ValueSetUtilities extends TerminologyUtilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static Set<String> codes(ValueSet vs, CodeSystem cs) {
|
||||
Set<String> res = new HashSet<>();
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
if (inc.getSystem().equals(cs.getUrl())) {
|
||||
addCodes(res, inc, cs.getConcept());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void addCodes(Set<String> res, ConceptSetComponent inc, List<ConceptDefinitionComponent> list) {
|
||||
for (ConceptDefinitionComponent cd : list) {
|
||||
if (cd.hasCode() && (!inc.hasConcept() || inc.hasConcept(cd.getCode()))) {
|
||||
res.add(cd.getCode());
|
||||
}
|
||||
if (cd.hasConcept()) {
|
||||
addCodes(res, inc, cd.getConcept());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ public interface ITerminologyClient {
|
||||
ValueSet expandValueset(ValueSet vs, Parameters p) throws FHIRException;
|
||||
Parameters validateCS(Parameters pin) throws FHIRException;
|
||||
Parameters validateVS(Parameters pin) throws FHIRException;
|
||||
Parameters subsumes(Parameters pin) throws FHIRException;
|
||||
ITerminologyClient setTimeoutFactor(int i) throws FHIRException;
|
||||
ToolingClientLogger getLogger();
|
||||
ITerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
|
||||
|
@ -153,7 +153,7 @@ public class TerminologyClientManager {
|
||||
for (String s : ol.authoritative) {
|
||||
boolean ok = true;
|
||||
for (ServerOptionList t : choices) {
|
||||
if (!t.candidates.contains(s)) {
|
||||
if (!t.authoritative.contains(s) && !t.candidates.contains(s)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,11 @@ public class TerminologyClientR5 implements ITerminologyClient {
|
||||
return client.operateType(CodeSystem.class, "validate-code", pin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters subsumes(Parameters pin) {
|
||||
return client.operateType(CodeSystem.class, "subsumes", pin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters validateVS(Parameters pin) {
|
||||
return client.operateType(ValueSet.class, "validate-code", pin);
|
||||
@ -256,4 +261,5 @@ public class TerminologyClientR5 implements ITerminologyClient {
|
||||
return client.search(type, criteria);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -148,6 +148,21 @@ public class TerminologyCache {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SubsumesResult {
|
||||
|
||||
private Boolean result;
|
||||
|
||||
protected SubsumesResult(Boolean result) {
|
||||
super();
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Boolean getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected SystemNameKeyGenerator getSystemNameKeyGenerator() {
|
||||
return systemNameKeyGenerator;
|
||||
}
|
||||
@ -218,6 +233,7 @@ public class TerminologyCache {
|
||||
private boolean persistent;
|
||||
private ValidationResult v;
|
||||
private ValueSetExpansionOutcome e;
|
||||
private SubsumesResult s;
|
||||
}
|
||||
|
||||
private class NamedCache {
|
||||
@ -381,9 +397,9 @@ public class TerminologyCache {
|
||||
if (code.hasSystem()) {
|
||||
ct.setName(code.getSystem());
|
||||
ct.hasVersion = code.hasVersion();
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ct.name = NAME_FOR_NO_SYSTEM;
|
||||
}
|
||||
ct.setName(vsUrl);
|
||||
JsonParser json = new JsonParser();
|
||||
json.setOutputStyle(OutputStyle.PRETTY);
|
||||
@ -633,6 +649,9 @@ public class TerminologyCache {
|
||||
if (ce.e.getValueset() != null)
|
||||
sw.write(" \"valueSet\" : "+json.composeString(ce.e.getValueset()).trim()+",\r\n");
|
||||
sw.write(" \"error\" : \""+Utilities.escapeJson(ce.e.getError()).trim()+"\"\r\n}\r\n");
|
||||
} else if (ce.s != null) {
|
||||
sw.write("s: {\r\n");
|
||||
sw.write(" \"result\" : "+ce.s.result+"\r\n}\r\n");
|
||||
} else {
|
||||
sw.write("v: {\r\n");
|
||||
boolean first = true;
|
||||
@ -743,15 +762,17 @@ public class TerminologyCache {
|
||||
CacheEntry ce = new CacheEntry();
|
||||
ce.persistent = true;
|
||||
ce.request = request;
|
||||
boolean e = resultString.charAt(0) == 'e';
|
||||
char e = resultString.charAt(0);
|
||||
resultString = resultString.substring(3);
|
||||
JsonObject o = (JsonObject) new com.google.gson.JsonParser().parse(resultString);
|
||||
String error = loadJS(o.get("error"));
|
||||
if (e) {
|
||||
if (e == 'e') {
|
||||
if (o.has("valueSet"))
|
||||
ce.e = new ValueSetExpansionOutcome((ValueSet) new JsonParser().parse(o.getAsJsonObject("valueSet")), error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
||||
else
|
||||
ce.e = new ValueSetExpansionOutcome(error, TerminologyServiceErrorClass.UNKNOWN, o.has("from-server"));
|
||||
} else if (e == 's') {
|
||||
ce.s = new SubsumesResult(o.get("result").getAsBoolean());
|
||||
} else {
|
||||
String t = loadJS(o.get("severity"));
|
||||
IssueSeverity severity = t == null ? null : IssueSeverity.fromCode(t);
|
||||
@ -934,15 +955,15 @@ public class TerminologyCache {
|
||||
|
||||
public Map<String, String> servers() {
|
||||
Map<String, String> servers = new HashMap<>();
|
||||
servers.put("http://local.fhir.org/r2", "tx.fhir.org");
|
||||
servers.put("http://local.fhir.org/r3", "tx.fhir.org");
|
||||
servers.put("http://local.fhir.org/r4", "tx.fhir.org");
|
||||
servers.put("http://local.fhir.org/r5", "tx.fhir.org");
|
||||
|
||||
servers.put("http://tx-dev.fhir.org/r2", "tx.fhir.org");
|
||||
servers.put("http://tx-dev.fhir.org/r3", "tx.fhir.org");
|
||||
servers.put("http://tx-dev.fhir.org/r4", "tx.fhir.org");
|
||||
servers.put("http://tx-dev.fhir.org/r5", "tx.fhir.org");
|
||||
// servers.put("http://local.fhir.org/r2", "tx.fhir.org");
|
||||
// servers.put("http://local.fhir.org/r3", "tx.fhir.org");
|
||||
// servers.put("http://local.fhir.org/r4", "tx.fhir.org");
|
||||
// servers.put("http://local.fhir.org/r5", "tx.fhir.org");
|
||||
//
|
||||
// servers.put("http://tx-dev.fhir.org/r2", "tx.fhir.org");
|
||||
// servers.put("http://tx-dev.fhir.org/r3", "tx.fhir.org");
|
||||
// servers.put("http://tx-dev.fhir.org/r4", "tx.fhir.org");
|
||||
// servers.put("http://tx-dev.fhir.org/r5", "tx.fhir.org");
|
||||
|
||||
servers.put("http://tx.fhir.org/r2", "tx.fhir.org");
|
||||
servers.put("http://tx.fhir.org/r3", "tx.fhir.org");
|
||||
@ -1002,5 +1023,58 @@ public class TerminologyCache {
|
||||
}
|
||||
}
|
||||
|
||||
public CacheToken generateSubsumesToken(ValidationOptions options, Coding parent, Coding child, Parameters expParameters) {
|
||||
try {
|
||||
CacheToken ct = new CacheToken();
|
||||
if (parent.hasSystem()) {
|
||||
ct.setName(parent.getSystem());
|
||||
}
|
||||
if (child.hasSystem()) {
|
||||
ct.setName(child.getSystem());
|
||||
}
|
||||
ct.hasVersion = parent.hasVersion() || child.hasVersion();
|
||||
JsonParser json = new JsonParser();
|
||||
json.setOutputStyle(OutputStyle.PRETTY);
|
||||
String expJS = json.composeString(expParameters);
|
||||
ct.request = "{\"op\": \"subsumes\", \"parent\" : "+json.composeString(parent, "code")+", \"child\" :"+json.composeString(child, "code")+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}";
|
||||
ct.key = String.valueOf(hashJson(ct.request));
|
||||
return ct;
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean getSubsumes(CacheToken cacheToken) {
|
||||
if (cacheToken.key == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (lock) {
|
||||
requestCount++;
|
||||
NamedCache nc = getNamedCache(cacheToken);
|
||||
CacheEntry e = nc.map.get(cacheToken.key);
|
||||
if (e == null) {
|
||||
networkCount++;
|
||||
return null;
|
||||
} else {
|
||||
hitCount++;
|
||||
return e.s.result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void cacheSubsumes(CacheToken cacheToken, Boolean b, boolean persistent) {
|
||||
if (cacheToken.key != null) {
|
||||
synchronized (lock) {
|
||||
NamedCache nc = getNamedCache(cacheToken);
|
||||
CacheEntry e = new CacheEntry();
|
||||
e.request = cacheToken.request;
|
||||
e.persistent = persistent;
|
||||
e.s = new SubsumesResult(b);
|
||||
store(cacheToken, persistent, nc, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -481,7 +481,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
VersionInfo vi = new VersionInfo(this);
|
||||
checkCanonical(issues, path, valueset, valueset);
|
||||
|
||||
String system = code.hasSystem() ? code.getSystem() : getValueSetSystemOrNull();
|
||||
String system = code.getSystem();
|
||||
if (!options.isMembershipOnly()) {
|
||||
if (system == null && !code.hasDisplay() && options.isGuessSystem()) { // dealing with just a plain code (enum)
|
||||
List<StringWithCode> problems = new ArrayList<>();
|
||||
@ -490,7 +490,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
if (problems.size() == 0) {
|
||||
throw new Error("Unable to resolve systems but no reason why"); // this is an error in the java code
|
||||
} else if (problems.size() == 1) {
|
||||
return new ValidationResult(IssueSeverity.ERROR, problems.get(0).getMessage(), makeIssue(IssueSeverity.ERROR, IssueType.UNKNOWN, path, problems.get(0).getMessage(), problems.get(0).getCode(), null));
|
||||
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), "'"+code.toString()+"'");
|
||||
issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, "code", msg, OpIssueCode.NotInVS, null));
|
||||
issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, "code", problems.get(0).getMessage(), problems.get(0).getCode(), null));
|
||||
return new ValidationResult(IssueSeverity.ERROR, problems.get(0).getMessage()+"; "+msg, issues);
|
||||
} else {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("; ");
|
||||
for (StringWithCode s : problems) {
|
||||
@ -835,7 +838,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
}
|
||||
|
||||
private ValidationResult validateCode(String path, Coding code, CodeSystem cs, CodeableConcept vcc, ValidationProcessInfo info) {
|
||||
ConceptDefinitionComponent cc = cs.hasUserData("tx.cs.special") ? ((SpecialCodeSystem) cs.getUserData("tx.cs.special")).findConcept(code) : findCodeInConcept(cs.getConcept(), code.getCode(), allAltCodes);
|
||||
ConceptDefinitionComponent cc = cs.hasUserData("tx.cs.special") ? ((SpecialCodeSystem) cs.getUserData("tx.cs.special")).findConcept(code) : findCodeInConcept(cs.getConcept(), code.getCode(), cs.getCaseSensitive(), allAltCodes);
|
||||
if (cc == null) {
|
||||
cc = findSpecialConcept(code, cs);
|
||||
}
|
||||
@ -847,6 +850,11 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODE_IN_VERSION, code.getCode(), cs.getUrl(), cs.getVersion());
|
||||
return new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path+".code", msg, OpIssueCode.InvalidCode, null));
|
||||
}
|
||||
} else {
|
||||
if (!cc.getCode().equals(code.getCode())) {
|
||||
String msg = context.formatMessage(I18nConstants.CODE_CASE_DIFFERENCE, code.getCode(), cc.getCode(), cs.getVersionedUrl());
|
||||
info.addIssue(makeIssue(IssueSeverity.INFORMATION, IssueType.BUSINESSRULE, path+".code", msg, OpIssueCode.CodeRule, null));
|
||||
}
|
||||
}
|
||||
Coding vc = new Coding().setCode(cc.getCode()).setSystem(cs.getUrl()).setVersion(cs.getVersion()).setDisplay(getPreferredDisplay(cc, cs));
|
||||
if (vcc != null) {
|
||||
@ -986,40 +994,6 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getValueSetSystemOrNull() throws FHIRException {
|
||||
if (valueset == null) {
|
||||
return null;
|
||||
}
|
||||
if (!options.isGuessSystem()) {
|
||||
return null;
|
||||
}
|
||||
if (valueset.getCompose().getInclude().size() == 0) {
|
||||
if (!valueset.hasExpansion() || valueset.getExpansion().getContains().size() == 0) {
|
||||
return null;
|
||||
} else {
|
||||
String cs = valueset.getExpansion().getContains().get(0).getSystem();
|
||||
if (cs != null && checkSystem(valueset.getExpansion().getContains(), cs)) {
|
||||
return cs;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
|
||||
if (inc.hasValueSet()) {
|
||||
return null;
|
||||
}
|
||||
if (!inc.hasSystem()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (valueset.getCompose().getInclude().size() == 1) {
|
||||
return valueset.getCompose().getInclude().get(0).getSystem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that all system values within an expansion correspond to the specified system value
|
||||
*/
|
||||
@ -1032,19 +1006,19 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code, AlternateCodesProcessingRules altCodeRules) {
|
||||
private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code, boolean caseSensitive, AlternateCodesProcessingRules altCodeRules) {
|
||||
opContext.deadCheck();
|
||||
if (code.equals(concept.getCode())) {
|
||||
return concept;
|
||||
}
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(concept.getConcept(), code, altCodeRules);
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(concept.getConcept(), code, caseSensitive, altCodeRules);
|
||||
if (cc != null) {
|
||||
return cc;
|
||||
}
|
||||
if (concept.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
|
||||
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) concept.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
|
||||
for (ConceptDefinitionComponent c : children) {
|
||||
cc = findCodeInConcept(c, code, altCodeRules);
|
||||
cc = findCodeInConcept(c, code, caseSensitive, altCodeRules);
|
||||
if (cc != null) {
|
||||
return cc;
|
||||
}
|
||||
@ -1053,15 +1027,15 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConceptDefinitionComponent findCodeInConcept(List<ConceptDefinitionComponent> concept, String code, AlternateCodesProcessingRules altCodeRules) {
|
||||
private ConceptDefinitionComponent findCodeInConcept(List<ConceptDefinitionComponent> concept, String code, boolean caseSensitive, AlternateCodesProcessingRules altCodeRules) {
|
||||
for (ConceptDefinitionComponent cc : concept) {
|
||||
if (code.equals(cc.getCode())) {
|
||||
if (code.equals(cc.getCode()) || (!caseSensitive && (code.equalsIgnoreCase(cc.getCode())))) {
|
||||
return cc;
|
||||
}
|
||||
if (Utilities.existsInList(code, alternateCodes(cc, altCodeRules))) {
|
||||
return cc;
|
||||
}
|
||||
ConceptDefinitionComponent c = findCodeInConcept(cc, code, altCodeRules);
|
||||
ConceptDefinitionComponent c = findCodeInConcept(cc, code, caseSensitive, altCodeRules);
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
@ -1087,10 +1061,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
return null;
|
||||
}
|
||||
if (sys.size() == 0) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_NO_MATCHES, code)));
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_NO_MATCHES, code, valueset.getVersionedUrl())));
|
||||
return null;
|
||||
} else if (sys.size() > 1) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_MULTIPLE_MATCHES, sys.toString())));
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_MULTIPLE_MATCHES, code, valueset.getVersionedUrl(), sys.toString())));
|
||||
return null;
|
||||
} else {
|
||||
return sys.iterator().next();
|
||||
@ -1101,7 +1075,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
if (valueset.hasCompose()) {
|
||||
// ignore excludes - they can't make any difference
|
||||
if (!valueset.getCompose().hasInclude() && !valueset.getExpansion().hasContains()) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_NO_INCLUDES_OR_EXPANSION, valueset.getVersionedUrl())));
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_NO_INCLUDES_OR_EXPANSION, code, valueset.getVersionedUrl())));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@ -1114,19 +1088,29 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
}
|
||||
}
|
||||
} else if (!vsi.hasSystem()) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_NO_SYSTEM, valueset.getVersionedUrl(), i)));
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_NO_SYSTEM, code, valueset.getVersionedUrl(), i)));
|
||||
return false;
|
||||
}
|
||||
if (vsi.hasSystem()) {
|
||||
if (vsi.hasFilter()) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_FILTER, valueset.getVersionedUrl(), i, vsi.getSystem())));
|
||||
return false;
|
||||
ValueSet vsDummy = new ValueSet();
|
||||
vsDummy.setUrl(Utilities.makeUuidUrn());
|
||||
vsDummy.setStatus(PublicationStatus.ACTIVE);
|
||||
vsDummy.getCompose().addInclude(vsi);
|
||||
Coding c = new Coding().setCode(code).setSystem(vsi.getSystem());
|
||||
ValidationResult vr = context.validateCode(options.withGuessSystem(false), c, vsDummy);
|
||||
if (vr.isOk()) {
|
||||
sys.add(vsi.getSystem());
|
||||
} else {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_FILTER, code, valueset.getVersionedUrl(), i, vsi.getSystem())));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CodeSystemProvider csp = CodeSystemProvider.factory(vsi.getSystem());
|
||||
if (csp != null) {
|
||||
Boolean ok = csp.checkCode(code);
|
||||
if (ok == null) {
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE, valueset.getVersionedUrl(), vsi.getSystem())));
|
||||
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE, code, valueset.getVersionedUrl(), vsi.getSystem())));
|
||||
sys.add(vsi.getSystem());
|
||||
} else if (ok) {
|
||||
sys.add(vsi.getSystem());
|
||||
@ -1143,7 +1127,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code, allAltCodes);
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code, cs.getCaseSensitive(), allAltCodes);
|
||||
if (cc != null) {
|
||||
sys.add(vsi.getSystem());
|
||||
}
|
||||
@ -1156,15 +1140,25 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we'll try to expand this one then
|
||||
ValueSetExpansionOutcome vse = context.expandVS(vsi, false, false);
|
||||
if (vse.isOk()) {
|
||||
if (!checkSystems(vse.getValueset().getExpansion().getContains(), code, sys, problems)) {
|
||||
ValueSet vsDummy = new ValueSet();
|
||||
vsDummy.setUrl(Utilities.makeUuidUrn());
|
||||
vsDummy.setStatus(PublicationStatus.ACTIVE);
|
||||
vsDummy.getCompose().addInclude(vsi);
|
||||
ValidationResult vr = context.validateCode(options.withNoClient(), code, vsDummy);
|
||||
if (vr.isOk()) {
|
||||
sys.add(vsi.getSystem());
|
||||
} else {
|
||||
// ok, we'll try to expand this one then
|
||||
ValueSetExpansionOutcome vse = context.expandVS(vsi, false, false);
|
||||
if (vse.isOk()) {
|
||||
if (!checkSystems(vse.getValueset().getExpansion().getContains(), code, sys, problems)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
problems.add(new StringWithCode(OpIssueCode.NotFound, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_UNKNOWN_SYSTEM, code, valueset.getVersionedUrl(), i, vsi.getSystem(), vse.getAllErrors().toString())));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
problems.add(new StringWithCode(OpIssueCode.NotFound, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_UNKNOWN_SYSTEM, valueset.getVersionedUrl(), i, vsi.getSystem(), vse.getAllErrors().toString())));
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1420,11 +1414,11 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
||||
if (!excludeRoot && code.equals(f.getValue())) {
|
||||
return true;
|
||||
}
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue(), altCodeParams);
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue(), cs.getCaseSensitive(), altCodeParams);
|
||||
if (cc == null) {
|
||||
return false;
|
||||
}
|
||||
ConceptDefinitionComponent cc2 = findCodeInConcept(cc, code, altCodeParams);
|
||||
ConceptDefinitionComponent cc2 = findCodeInConcept(cc, code, cs.getCaseSensitive(), altCodeParams);
|
||||
return cc2 != null && cc2 != cc;
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,12 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
|
||||
private static final boolean SHOW_DIFF = false;
|
||||
private JsonObject externals;
|
||||
|
||||
|
||||
public String createNotEqualMessage(final String message, final String expected, final String actual) {
|
||||
return new StringBuilder()
|
||||
.append(message).append('\n')
|
||||
.append("Expected :").append(presentExpected(expected)).append('\n')
|
||||
.append("Actual :").append("\""+actual+"\"").toString();
|
||||
.append(message).append('\n')
|
||||
.append("Expected :").append(presentExpected(expected)).append('\n')
|
||||
.append("Actual :").append("\""+actual+"\"").toString();
|
||||
}
|
||||
|
||||
private String presentExpected(String expected) {
|
||||
@ -86,7 +86,7 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String getDiffTool() throws IOException {
|
||||
private static String getDiffTool() throws IOException {
|
||||
if (FhirSettings.hasDiffToolPath()) {
|
||||
return FhirSettings.getDiffToolPath();
|
||||
} else if (System.getenv("ProgramFiles") != null) {
|
||||
@ -185,21 +185,21 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] unBase64(String text) {
|
||||
private byte[] unBase64(String text) {
|
||||
return Base64.decodeBase64(text);
|
||||
}
|
||||
|
||||
private Node skipBlankText(Node node) {
|
||||
private Node skipBlankText(Node node) {
|
||||
while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && StringUtils.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE)))
|
||||
node = node.getNextSibling();
|
||||
return node;
|
||||
}
|
||||
|
||||
private Document loadXml(String fn) throws Exception {
|
||||
private Document loadXml(String fn) throws Exception {
|
||||
return loadXml(new FileInputStream(fn));
|
||||
}
|
||||
|
||||
private Document loadXml(InputStream fn) throws Exception {
|
||||
private Document loadXml(InputStream fn) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
@ -361,7 +361,7 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
} else if (actualJsonElement instanceof JsonArray) {
|
||||
JsonArray actualArray = (JsonArray) actualJsonElement;
|
||||
JsonArray expectedArray = (JsonArray) expectedJsonElement;
|
||||
|
||||
|
||||
int as = actualArray.size();
|
||||
int es = expectedArray.size();
|
||||
if (countOnly) {
|
||||
@ -369,38 +369,38 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
return createNotEqualMessage("array item count differs at " + path, Integer.toString(es), Integer.toString(as));
|
||||
}
|
||||
} else {
|
||||
int expectedMin = countExpectedMin(expectedArray);
|
||||
int oc = optionalCount(expectedArray);
|
||||
|
||||
if (as > es || as < expectedMin)
|
||||
return createNotEqualMessage("array item count differs at " + path, Integer.toString(es), Integer.toString(as));
|
||||
int c = 0;
|
||||
for (int i = 0; i < es; i++) {
|
||||
if (c >= as) {
|
||||
if (i >= es - oc && isOptional(expectedArray.get(i))) {
|
||||
return null; // this is OK
|
||||
} else {
|
||||
return "One or more array items did not match at "+path+" starting at index "+i;
|
||||
int expectedMin = countExpectedMin(expectedArray);
|
||||
int oc = optionalCount(expectedArray);
|
||||
|
||||
if (as > es || as < expectedMin)
|
||||
return createNotEqualMessage("array item count differs at " + path, Integer.toString(es), Integer.toString(as));
|
||||
int c = 0;
|
||||
for (int i = 0; i < es; i++) {
|
||||
if (c >= as) {
|
||||
if (i >= es - oc && isOptional(expectedArray.get(i))) {
|
||||
return null; // this is OK
|
||||
} else {
|
||||
return "One or more array items did not match at "+path+" starting at index "+i;
|
||||
}
|
||||
}
|
||||
String s = compareNodes(path + "[" + Integer.toString(i) + "]", expectedArray.get(i), actualArray.get(c), false);
|
||||
if (!Utilities.noString(s) && !isOptional(expectedArray.get(i))) {
|
||||
return s;
|
||||
}
|
||||
if (Utilities.noString(s)) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
String s = compareNodes(path + "[" + Integer.toString(i) + "]", expectedArray.get(i), actualArray.get(c), false);
|
||||
if (!Utilities.noString(s) && !isOptional(expectedArray.get(i))) {
|
||||
return s;
|
||||
if (c < as) {
|
||||
return "Unexpected Node found in array at '"+path+"' at index "+c;
|
||||
}
|
||||
if (Utilities.noString(s)) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
if (c < as) {
|
||||
return "Unexpected Node found in array at index "+c;
|
||||
}
|
||||
}
|
||||
} else
|
||||
return "unhandled property " + actualJsonElement.getClass().getName();
|
||||
return null;
|
||||
}
|
||||
|
||||
private int optionalCount(JsonArray arr) {
|
||||
private int optionalCount(JsonArray arr) {
|
||||
int c = 0;
|
||||
for (JsonElement e : arr) {
|
||||
if (e.isJsonObject()) {
|
||||
@ -413,11 +413,11 @@ public class CompareUtilities extends BaseTestingUtilities {
|
||||
return c;
|
||||
}
|
||||
|
||||
private boolean isOptional(JsonElement e) {
|
||||
private boolean isOptional(JsonElement e) {
|
||||
return e.isJsonObject() && e.asJsonObject().has("$optional$");
|
||||
}
|
||||
|
||||
private int countExpectedMin(JsonArray array) {
|
||||
private int countExpectedMin(JsonArray array) {
|
||||
int count = array.size();
|
||||
for (JsonElement e : array) {
|
||||
if (isOptional(e)) {
|
||||
@ -470,7 +470,7 @@ private boolean isOptional(JsonElement e) {
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> readChoices(String s) {
|
||||
private List<String> readChoices(String s) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String p : s.split("\\|")) {
|
||||
list.add(p);
|
||||
@ -518,7 +518,7 @@ private boolean isOptional(JsonElement e) {
|
||||
}
|
||||
|
||||
|
||||
private String compareText(String expectedString, String actualString) {
|
||||
private String compareText(String expectedString, String actualString) {
|
||||
for (int i = 0; i < Integer.min(expectedString.length(), actualString.length()); i++) {
|
||||
if (expectedString.charAt(i) != actualString.charAt(i))
|
||||
return createNotEqualMessage("Strings differ at character " + Integer.toString(i), charWithContext(expectedString, i), charWithContext(actualString, i));
|
||||
@ -528,19 +528,19 @@ private boolean isOptional(JsonElement e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String charWithContext(String s, int i) {
|
||||
String result = s.substring(i, i+1);
|
||||
if (i > 7) {
|
||||
i = i - 7;
|
||||
private String charWithContext(String s, int i) {
|
||||
String result = s.substring(i, i+1);
|
||||
if (i > 7) {
|
||||
i = i - 7;
|
||||
}
|
||||
int e = i + 20;
|
||||
if (e > s.length()) {
|
||||
e = s.length();
|
||||
}
|
||||
if (e > i+1) {
|
||||
result = result + " with context '"+s.substring(i, e)+"'";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int e = i + 20;
|
||||
if (e > s.length()) {
|
||||
e = s.length();
|
||||
}
|
||||
if (e > i+1) {
|
||||
result = result + " with context '"+s.substring(i, e)+"'";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ public class XVerExtensionManager {
|
||||
|
||||
public static final String XVER_EXT_MARKER = "XVER_EXT_MARKER";
|
||||
|
||||
public static final String XVER_VER_MARKER = "XVER_VER_MARKER";
|
||||
|
||||
private Map<String, JsonObject> lists = new HashMap<>();
|
||||
private IWorkerContext context;
|
||||
|
||||
@ -92,6 +94,7 @@ public class XVerExtensionManager {
|
||||
|
||||
StructureDefinition sd = new StructureDefinition();
|
||||
sd.setUserData(XVER_EXT_MARKER, "true");
|
||||
sd.setUserData(XVER_VER_MARKER, verSource);
|
||||
if (context.getResourceNamesAsSet().contains(r)) {
|
||||
sd.setWebPath(Utilities.pathURL(context.getSpecUrl(), r.toLowerCase()+"-definitions.html#"+e));
|
||||
} else {
|
||||
|
@ -143,11 +143,11 @@ public class StructureMapUtilities {
|
||||
|
||||
public static String render(StructureMap map) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("map \"");
|
||||
b.append(map.getUrl());
|
||||
b.append("\" = \"");
|
||||
b.append(Utilities.escapeJson(map.getName()));
|
||||
b.append("\"\r\n\r\n");
|
||||
b.append("/// url = \""+map.getUrl()+"\"\r\n");
|
||||
b.append("/// name = \""+map.getName()+"\"\r\n");
|
||||
b.append("/// title = \""+map.getTitle()+"\"\r\n");
|
||||
b.append("/// status = \""+map.getStatus().toCode()+"\"\r\n");
|
||||
b.append("\r\n");
|
||||
if (map.getDescription() != null) {
|
||||
renderMultilineDoco(b, map.getDescription(), 0);
|
||||
b.append("\r\n");
|
||||
@ -385,7 +385,7 @@ public class StructureMapUtilities {
|
||||
for (int i = 0; i < indent; i++)
|
||||
b.append(' ');
|
||||
b.append("}");
|
||||
} else {
|
||||
} else if (!canBeAbbreviated) {
|
||||
if (r.hasDependent()) {
|
||||
b.append(" then ");
|
||||
boolean first = true;
|
||||
@ -450,7 +450,7 @@ public class StructureMapUtilities {
|
||||
return
|
||||
(r.getSource().size() == 1 && r.getSourceFirstRep().hasElement() && r.getSourceFirstRep().hasVariable()) &&
|
||||
(r.getTarget().size() == 1 && r.getTargetFirstRep().hasVariable() && (r.getTargetFirstRep().getTransform() == null || r.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE) && r.getTargetFirstRep().getParameter().size() == 0) &&
|
||||
(r.getDependent().size() == 0) && (r.getRule().size() == 0);
|
||||
(r.getDependent().size() == 0 || (r.getDependent().size() == 1 && StructureMapUtilities.DEF_GROUP_NAME.equals(r.getDependentFirstRep().getName()))) && (r.getRule().size() == 0);
|
||||
}
|
||||
|
||||
public static String sourceToString(StructureMapGroupRuleSourceComponent r) {
|
||||
@ -655,6 +655,9 @@ public class StructureMapUtilities {
|
||||
case "title" :
|
||||
result.setTitle(lexer.readConstant("title"));
|
||||
break;
|
||||
case "description" :
|
||||
result.setTitle(lexer.readConstant("description"));
|
||||
break;
|
||||
case "status" :
|
||||
result.setStatus(PublicationStatus.fromCode(lexer.readConstant("status")));
|
||||
break;
|
||||
|
@ -34,6 +34,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.UsageContext;
|
||||
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
|
||||
import org.hl7.fhir.r5.utils.validation.constants.CheckDisplayOption;
|
||||
import org.hl7.fhir.r5.utils.validation.constants.IdStatus;
|
||||
@ -105,6 +106,9 @@ public interface IResourceValidator {
|
||||
boolean isExample();
|
||||
IResourceValidator setExample(boolean example);
|
||||
|
||||
// used to decide whether additional bindings, constraints etc apply
|
||||
public List<UsageContext> getUsageContexts();
|
||||
|
||||
public boolean isWarnOnDraftOrExperimental();
|
||||
|
||||
public IResourceValidator setWarnOnDraftOrExperimental(boolean warnOnDraftOrExperimental);
|
||||
|
@ -123,6 +123,16 @@ public class SimpleWorkerContextTests {
|
||||
}
|
||||
}
|
||||
|
||||
public class CodingMatcher implements ArgumentMatcher<Coding> {
|
||||
final private Coding left;
|
||||
|
||||
CodingMatcher(Coding left) { this.left = left; }
|
||||
|
||||
public boolean matches(Coding right) {
|
||||
return left.equalsShallow(right);
|
||||
}
|
||||
}
|
||||
|
||||
public class ParametersMatcher implements ArgumentMatcher<Parameters> {
|
||||
final private Parameters left;
|
||||
|
||||
@ -187,7 +197,7 @@ public class SimpleWorkerContextTests {
|
||||
|
||||
assertEquals(expectedValidationResult, actualValidationResult);
|
||||
|
||||
Mockito.verify(valueSetCheckerSimple).validateCode("Coding", coding);
|
||||
Mockito.verify(valueSetCheckerSimple).validateCode(eq("Coding"), argThat(new CodingMatcher(coding)));
|
||||
Mockito.verify(terminologyCache).getValidation(cacheToken);
|
||||
Mockito.verify(terminologyCache).cacheValidation(cacheToken, expectedValidationResult,false);
|
||||
}
|
||||
@ -325,7 +335,7 @@ public class SimpleWorkerContextTests {
|
||||
|
||||
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(inc, true, false);
|
||||
|
||||
// assertEquals(expectedValueSet, actualExpansionResult.getValueset());
|
||||
assertEquals(expectedValueSet, actualExpansionResult.getValueset());
|
||||
|
||||
Mockito.verify(terminologyCache).getExpansion(cacheToken);
|
||||
Mockito.verify(terminologyCache).cacheExpansion(cacheToken, actualExpansionResult,true);
|
||||
@ -412,7 +422,7 @@ public class SimpleWorkerContextTests {
|
||||
|
||||
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn, false);
|
||||
|
||||
// assertEquals(expectedValueSet, actualExpansionResult.getValueset());
|
||||
assertEquals(expectedValueSet, actualExpansionResult.getValueset());
|
||||
|
||||
Mockito.verify(terminologyCache).getExpansion(cacheToken);
|
||||
Mockito.verify(terminologyCache).cacheExpansion(cacheToken, actualExpansionResult, true);
|
||||
@ -421,40 +431,37 @@ public class SimpleWorkerContextTests {
|
||||
|
||||
@Test
|
||||
public void testInitializationWithCache() {
|
||||
// String address = "/...";
|
||||
//
|
||||
// Mockito.doReturn(true).when(terminologyCache).hasTerminologyCapabilities(address);
|
||||
//// Mockito.doReturn(true).when(terminologyCache).hasCapabilityStatement();
|
||||
//
|
||||
// Mockito.doReturn(terminologyCapabilities).when(terminologyCache).getTerminologyCapabilities(address);
|
||||
//// Mockito.doReturn(capabilitiesStatement).when(terminologyCache).getCapabilityStatement();
|
||||
//
|
||||
// context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
|
||||
//
|
||||
// Mockito.verify(terminologyCache).getTerminologyCapabilities(address);
|
||||
// Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
|
||||
//
|
||||
// Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
|
||||
// Mockito.verify(terminologyClient, times(0)).getTerminologyCapabilities();
|
||||
String address = "dummyUrl";
|
||||
|
||||
Mockito.doReturn(true).when(terminologyCache).hasTerminologyCapabilities(address);
|
||||
|
||||
Mockito.doReturn(terminologyCapabilities).when(terminologyCache).getTerminologyCapabilities(address);
|
||||
|
||||
context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
|
||||
|
||||
Mockito.verify(terminologyCache).getTerminologyCapabilities(address);
|
||||
Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
|
||||
|
||||
Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
|
||||
Mockito.verify(terminologyClient, times(0)).getTerminologyCapabilities();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializationWithClient() {
|
||||
// String address = "/...";
|
||||
//
|
||||
// Mockito.doReturn(false).when(terminologyCache).hasTerminologyCapabilities(address);
|
||||
//// Mockito.doReturn(false).when(terminologyCache).hasCapabilityStatement();
|
||||
//
|
||||
// Mockito.doReturn(terminologyCapabilities).when(terminologyClient).getTerminologyCapabilities();
|
||||
// Mockito.doReturn(capabilitiesStatement).when(terminologyClient).getCapabilitiesStatementQuick();
|
||||
//
|
||||
// context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
|
||||
//
|
||||
// Mockito.verify(terminologyCache, times(0)).getTerminologyCapabilities(address);
|
||||
// Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
|
||||
//
|
||||
// Mockito.verify(terminologyClient).getTerminologyCapabilities();
|
||||
// Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
|
||||
String address = "dummyUrl";
|
||||
|
||||
Mockito.doReturn(false).when(terminologyCache).hasTerminologyCapabilities(address);
|
||||
|
||||
Mockito.doReturn(terminologyCapabilities).when(terminologyClient).getTerminologyCapabilities();
|
||||
Mockito.doReturn(capabilitiesStatement).when(terminologyClient).getCapabilitiesStatementQuick();
|
||||
|
||||
context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
|
||||
|
||||
Mockito.verify(terminologyCache, times(0)).getTerminologyCapabilities(address);
|
||||
Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
|
||||
|
||||
Mockito.verify(terminologyClient).getTerminologyCapabilities();
|
||||
Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
|
||||
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class StructureMapUtilitiesTest implements ITransformerServices {
|
||||
|
||||
private void assertSerializeDeserialize(StructureMap structureMap) {
|
||||
Assertions.assertEquals("syntax", structureMap.getName());
|
||||
Assertions.assertEquals("Title of this map\r\nAuthor", structureMap.getDescription());
|
||||
Assertions.assertEquals("description", structureMap.getDescription());
|
||||
Assertions.assertEquals("http://github.com/FHIR/fhir-test-cases/r5/fml/syntax", structureMap.getUrl());
|
||||
Assertions.assertEquals("Patient", structureMap.getStructure().get(0).getAlias());
|
||||
Assertions.assertEquals("http://hl7.org/fhir/StructureDefinition/Patient", structureMap.getStructure().get(0).getUrl());
|
||||
|
@ -0,0 +1,199 @@
|
||||
package org.hl7.fhir.r5.test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.fhir.ucum.UcumException;
|
||||
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.elementmodel.ResourceParser;
|
||||
import org.hl7.fhir.r5.elementmodel.TurtleParser;
|
||||
import org.hl7.fhir.r5.elementmodel.XmlParser;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
import org.hl7.fhir.utilities.turtle.Turtle;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* TurtleGeneratorTests
|
||||
* Generates turtle files from specified resources, including example "instances"
|
||||
* Unit tests for the generated turtle files
|
||||
* For generic RDF parsing tests, see `TurtleTests.java`
|
||||
* For ShEx validation tests, see `ShExGeneratorTests.java`
|
||||
* Author: Tim Prudhomme <tmprdh@gmail.com>
|
||||
*/
|
||||
public class TurtleGeneratorTests {
|
||||
|
||||
private static IWorkerContext workerContext;
|
||||
private static ResourceParser resourceParser;
|
||||
private static XmlParser xmlParser;
|
||||
private static TurtleParser turtleParser;
|
||||
|
||||
private static Path inputXmlDirectory;
|
||||
private static Path outputTurtleDirectory;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
workerContext = TestingUtilities.getSharedWorkerContext();
|
||||
resourceParser = new org.hl7.fhir.r5.elementmodel.ResourceParser(workerContext);
|
||||
xmlParser = (XmlParser) Manager.makeParser(workerContext, FhirFormat.XML);
|
||||
turtleParser = (TurtleParser) Manager.makeParser(workerContext, FhirFormat.TURTLE);
|
||||
|
||||
// Temporary directory of files that should be discarded after testing
|
||||
outputTurtleDirectory = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
// Directory of XML files used for generating Turtle files
|
||||
String currentDirectory = System.getProperty("user.dir");
|
||||
inputXmlDirectory = FileSystems.getDefault().getPath(currentDirectory, "src", "test", "resources", "testUtilities", "xml", "examples");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExamples() throws IOException, UcumException {
|
||||
var exampleInstanceName = "codesystem-contact-point-use";
|
||||
testInstanceGeneration(exampleInstanceName);
|
||||
}
|
||||
|
||||
@Disabled("TODO this doesn't pass due to existing issues in R5 RDF")
|
||||
@Test
|
||||
public void testProfiles() throws IOException, UcumException {
|
||||
var profileName = "Encounter";
|
||||
testClassGeneration(profileName);
|
||||
}
|
||||
|
||||
@Disabled("Run manually for testing with XML resources generated from FHIR specification publishing library")
|
||||
@Test
|
||||
public void testPublishedExamples() throws IOException, UcumException {
|
||||
inputXmlDirectory = getPublishedXmlDirectory();
|
||||
var exampleInstanceName = "codesystem-contact-point-use";
|
||||
testInstanceGeneration(exampleInstanceName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a Turtle file from the name of an XML resource, then parse it
|
||||
*/
|
||||
private void testInstanceGeneration(String resourceName) throws IOException, UcumException {
|
||||
// Generate Turtle
|
||||
var generatedTurtleFilePath = generateTurtleFromResourceName(resourceName, inputXmlDirectory, outputTurtleDirectory);
|
||||
// Try parsing again ("round-trip test") -- this only tests for valid RDF
|
||||
parseGeneratedTurtle(generatedTurtleFilePath);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a Turtle file from the name of a profile, then parse it
|
||||
*/
|
||||
private void testClassGeneration(String profileName) throws IOException, UcumException {
|
||||
var generatedTurtleFilePath = generateTurtleClassFromProfileName(profileName);
|
||||
// Try parsing again ("round-trip test") -- this only tests for valid RDF
|
||||
parseGeneratedTurtle(generatedTurtleFilePath);
|
||||
}
|
||||
|
||||
private void parseGeneratedTurtle(String generatedTurtleFilePath) throws IOException {
|
||||
try (
|
||||
InputStream turtleStream = new FileInputStream(generatedTurtleFilePath);
|
||||
) {
|
||||
var generatedTurtleString = new String(turtleStream.readAllBytes());
|
||||
Turtle ttl = new Turtle();
|
||||
ttl.parse(generatedTurtleString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Turtle version of a resource, given its name, input directory of its XML source, and output directory of the Turtle file
|
||||
* @return the path of the generated Turtle file
|
||||
*/
|
||||
private String generateTurtleFromResourceName(String resourceName, Path inputXmlDirectory, Path outputTurtleDirectory) throws IOException, UcumException {
|
||||
// Specify source xml path and destination turtle path
|
||||
var xmlFilePath = inputXmlDirectory.resolve(resourceName + ".xml").toString();
|
||||
var turtleFilePath = outputTurtleDirectory.resolve(resourceName + ".ttl").toString();
|
||||
try (
|
||||
InputStream inputXmlStream = new FileInputStream(xmlFilePath);
|
||||
OutputStream outputTurtleStream = new FileOutputStream(turtleFilePath);
|
||||
) {
|
||||
// print out file names using string interpolation
|
||||
System.out.println("Generating " + turtleFilePath);
|
||||
generateTurtleFromXmlStream(inputXmlStream, outputTurtleStream);
|
||||
return turtleFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Turtle file from an XML resource
|
||||
*/
|
||||
private void generateTurtleFromXmlStream(InputStream xmlStream, OutputStream turtleStream) throws IOException, UcumException {
|
||||
var errorList = new ArrayList<ValidationMessage>();
|
||||
Element resourceElement = xmlParser.parseSingle(xmlStream, errorList);
|
||||
turtleParser.compose(resourceElement, turtleStream, OutputStyle.PRETTY, null);
|
||||
// Check errors
|
||||
for (ValidationMessage m : errorList) {
|
||||
System.out.println(m.getDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Turtle file from an org.hl7.fhir.r5.model.Resource profile
|
||||
* @return the path of the generated Turtle file
|
||||
*/
|
||||
private String generateTurtleClassFromProfileName(String profileName) throws IOException, UcumException {
|
||||
String resourceUri = ProfileUtilities.sdNs(profileName, null);
|
||||
Resource resource = workerContext.fetchResource(Resource.class, resourceUri);
|
||||
Element resourceElement = resourceParser.parse(resource);
|
||||
var turtleFilePath = outputTurtleDirectory.resolve(profileName + ".ttl").toString();
|
||||
try (OutputStream outputStream = new FileOutputStream(turtleFilePath)) {
|
||||
turtleParser.compose(resourceElement, outputStream, OutputStyle.PRETTY, null);
|
||||
return turtleFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Turtle file from a "test case" resource -- those only available on https://github.com/FHIR/fhir-test-cases/
|
||||
* @return the path of the generated Turtle file
|
||||
*/
|
||||
private String generateTurtleFromTestCaseResource(String resourceName) throws IOException, UcumException {
|
||||
var turtleFilePath = outputTurtleDirectory.resolve(resourceName + ".ttl").toString();
|
||||
try (
|
||||
// Follows pattern in `TestingUtilities.java`
|
||||
InputStream inputXmlStream = TestingUtilities.loadTestResourceStream("r5", resourceName + ".xml");
|
||||
OutputStream outputTurtleStream = new FileOutputStream(turtleFilePath);
|
||||
) {
|
||||
generateTurtleFromXmlStream(inputXmlStream, outputTurtleStream);
|
||||
return turtleFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This could be the "publish" directory of XML resources built using the FHIR specification publishing library.
|
||||
* Use this for testing with other generated XML resources
|
||||
*/
|
||||
private static Path getPublishedXmlDirectory() throws IOException {
|
||||
Properties properties = new Properties();
|
||||
String currentDirectory = System.getProperty("user.dir");
|
||||
// Add your directory path to "org.hl7.fhir.r5/src/test/resources/local.properties"
|
||||
String localPropertiesPath = FileSystems.getDefault().getPath(currentDirectory, "src", "test", "resources", "local.properties").toString();
|
||||
try (FileInputStream input = new FileInputStream(localPropertiesPath)) {
|
||||
properties.load(input);
|
||||
} catch (IOException e) {
|
||||
// You should create this local.properties file if it doesn't exist. It should already be listed in .gitignore.
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
var filePath = properties.getProperty("xmlResourceDirectory");
|
||||
return FileSystems.getDefault().getPath(filePath);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<CodeSystem xmlns="http://hl7.org/fhir">
|
||||
<id value="contact-point-use"/>
|
||||
<meta>
|
||||
<lastUpdated value="2023-10-03T22:51:29.574-04:00"/>
|
||||
</meta>
|
||||
<text>
|
||||
<status value="generated"/>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p>This case-sensitive code system
|
||||
<code>http://hl7.org/fhir/contact-point-use</code> defines the following codes:
|
||||
</p>
|
||||
<table class="codes">
|
||||
<tr>
|
||||
<td style="white-space:nowrap">
|
||||
<b>Code</b>
|
||||
</td>
|
||||
<td>
|
||||
<b>Display</b>
|
||||
</td>
|
||||
<td>
|
||||
<b>Definition</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space:nowrap">home
|
||||
<a name="contact-point-use-home"> </a>
|
||||
</td>
|
||||
<td>Home</td>
|
||||
<td>A communication contact point at a home; attempted contacts for business purposes might intrude privacy and chances are one will contact family or other household members instead of the person one wishes to call. Typically used with urgent cases, or if no other contacts are available.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space:nowrap">work
|
||||
<a name="contact-point-use-work"> </a>
|
||||
</td>
|
||||
<td>Work</td>
|
||||
<td>An office contact point. First choice for business related contacts during business hours.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space:nowrap">temp
|
||||
<a name="contact-point-use-temp"> </a>
|
||||
</td>
|
||||
<td>Temp</td>
|
||||
<td>A temporary contact point. The period can provide more detailed information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space:nowrap">old
|
||||
<a name="contact-point-use-old"> </a>
|
||||
</td>
|
||||
<td>Old</td>
|
||||
<td>This contact point is no longer in use (or was never correct, but retained for records).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space:nowrap">mobile
|
||||
<a name="contact-point-use-mobile"> </a>
|
||||
</td>
|
||||
<td>Mobile</td>
|
||||
<td>A telecommunication device that moves and stays with its owner. May have characteristics of all other use codes, suitable for urgent matters, not the first choice for routine business.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</text>
|
||||
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-wg">
|
||||
<valueCode value="fhir"/>
|
||||
</extension>
|
||||
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status">
|
||||
<valueCode value="normative"/>
|
||||
</extension>
|
||||
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version">
|
||||
<valueCode value="4.0.0"/>
|
||||
</extension>
|
||||
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm">
|
||||
<valueInteger value="5"/>
|
||||
</extension>
|
||||
<url value="http://hl7.org/fhir/contact-point-use"/>
|
||||
<identifier>
|
||||
<system value="urn:ietf:rfc:3986"/>
|
||||
<value value="urn:oid:2.16.840.1.113883.4.642.1.74"/>
|
||||
</identifier>
|
||||
<version value="6.0.0-cibuild"/>
|
||||
<name value="ContactPointUse"/>
|
||||
<title value="ContactPointUse"/>
|
||||
<status value="active"/>
|
||||
<experimental value="false"/>
|
||||
<date value="2023-10-03T22:51:29-04:00"/>
|
||||
<publisher value="HL7 (FHIR Project)"/>
|
||||
<contact>
|
||||
<telecom>
|
||||
<system value="url"/>
|
||||
<value value="http://hl7.org/fhir"/>
|
||||
</telecom>
|
||||
<telecom>
|
||||
<system value="email"/>
|
||||
<value value="fhir@lists.hl7.org"/>
|
||||
</telecom>
|
||||
</contact>
|
||||
<description value="Use of contact point."/>
|
||||
<jurisdiction>
|
||||
<coding>
|
||||
<system value="http://unstats.un.org/unsd/methods/m49/m49.htm"/>
|
||||
<code value="001"/>
|
||||
<display value="World"/>
|
||||
</coding>
|
||||
</jurisdiction>
|
||||
<caseSensitive value="true"/>
|
||||
<valueSet value="http://hl7.org/fhir/ValueSet/contact-point-use"/>
|
||||
<content value="complete"/>
|
||||
<concept>
|
||||
<code value="home"/>
|
||||
<display value="Home"/>
|
||||
<definition value="A communication contact point at a home; attempted contacts for business purposes might intrude privacy and chances are one will contact family or other household members instead of the person one wishes to call. Typically used with urgent cases, or if no other contacts are available."/>
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="work"/>
|
||||
<display value="Work"/>
|
||||
<definition value="An office contact point. First choice for business related contacts during business hours."/>
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="temp"/>
|
||||
<display value="Temp"/>
|
||||
<definition value="A temporary contact point. The period can provide more detailed information."/>
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="old"/>
|
||||
<display value="Old"/>
|
||||
<definition value="This contact point is no longer in use (or was never correct, but retained for records)."/>
|
||||
</concept>
|
||||
<concept>
|
||||
<code value="mobile"/>
|
||||
<display value="Mobile"/>
|
||||
<definition value="A telecommunication device that moves and stays with its owner. May have characteristics of all other use codes, suitable for urgent matters, not the first choice for routine business."/>
|
||||
</concept>
|
||||
</CodeSystem>
|
@ -161,4 +161,14 @@ public class CommaSeparatedStringBuilder {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static String joinWrapped(String sep, String leftWrap, String rightWrap, Collection<String> list) {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(sep);
|
||||
for (String s : list) {
|
||||
if (s != null) {
|
||||
b.append(leftWrap+s+rightWrap);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ public enum FhirPublication {
|
||||
case DSTU1: return "0.01";
|
||||
case DSTU2: return "1.0.2";
|
||||
case DSTU2016May: return "1.4.0";
|
||||
case STU3: return "3.0.1";
|
||||
case STU3: return "3.0.2";
|
||||
case R4: return "4.0.1";
|
||||
case R4B: return "4.3.0";
|
||||
case R5: return "5.0.0";
|
||||
|
@ -284,8 +284,15 @@ public class VersionUtilities {
|
||||
}
|
||||
|
||||
public static String getMajMin(String version) {
|
||||
if (version == null)
|
||||
if (version == null) {
|
||||
return null;
|
||||
}
|
||||
if (version.startsWith("http://hl7.org/fhir/")) {
|
||||
version = version.substring(20);
|
||||
if (version.contains("/")) {
|
||||
version = version.substring(0, version.indexOf("/"));
|
||||
}
|
||||
}
|
||||
|
||||
if (Utilities.charCount(version, '.') == 1) {
|
||||
String[] p = version.split("\\.");
|
||||
@ -729,5 +736,15 @@ public class VersionUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean includedInRange(String startVer, String stopVer, String ver) {
|
||||
if (ver.equals(startVer)) {
|
||||
return true;
|
||||
}
|
||||
if (ver.equals(stopVer)) {
|
||||
return true;
|
||||
}
|
||||
return startVer.compareTo(ver) < 0 && stopVer.compareTo(ver) > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -551,6 +551,8 @@ public class I18nConstants {
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URI_WS = "Type_Specific_Checks_DT_URI_WS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE = "Type_Specific_Checks_DT_URL_Resolve";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE = "Type_Specific_Checks_DT_XHTML_Resolve";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE_IMG = "Type_Specific_Checks_DT_XHTML_Resolve_Img";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE = "TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE";
|
||||
@ -1048,6 +1050,15 @@ public class I18nConstants {
|
||||
public static final String SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH = "SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH";
|
||||
public static final String TX_GENERAL_CC_ERROR_MESSAGE = "TX_GENERAL_CC_ERROR_MESSAGE";
|
||||
public static final String FHIRPATH_UNKNOWN_EXTENSION = "FHIRPATH_UNKNOWN_EXTENSION";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES = "TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES";
|
||||
public static final String CONTAINED_ORPHAN_DOM3 = "CONTAINED_ORPHAN_DOM3";
|
||||
public static final String VALUESET_INCLUDE_CS_NOT_CS = "VALUESET_INCLUDE_CS_NOT_CS";
|
||||
public static final String VALUESET_INCLUDE_CS_NOT_FOUND = "VALUESET_INCLUDE_CS_NOT_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CSVER_NOT_FOUND = "VALUESET_INCLUDE_CSVER_NOT_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CS_MULTI_FOUND = "VALUESET_INCLUDE_CS_MULTI_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CSVER_MULTI_FOUND = "VALUESET_INCLUDE_CSVER_MULTI_FOUND";
|
||||
public static final String UNABLE_TO_INFER_CODESYSTEM = "UNABLE_TO_INFER_CODESYSTEM";
|
||||
public static final String CODE_CASE_DIFFERENCE = "CODE_CASE_DIFFERENCE";
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,6 +158,13 @@ public class ValidationOptions {
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
public ValidationOptions withGuessSystem(boolean value) {
|
||||
ValidationOptions n = this.copy();
|
||||
n.guessSystem = value;
|
||||
return n;
|
||||
}
|
||||
|
||||
public ValidationOptions withActiveOnly() {
|
||||
ValidationOptions n = this.copy();
|
||||
n.activeOnly = true;
|
||||
@ -299,5 +306,6 @@ public class ValidationOptions {
|
||||
public FhirPublication getFhirVersion() {
|
||||
return fhirVersion;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -11,8 +11,10 @@ import org.hl7.fhir.utilities.Utilities;
|
||||
public abstract class XhtmlFluent {
|
||||
|
||||
protected abstract XhtmlNode addTag(String string);
|
||||
protected abstract XhtmlNode addTag(int index, String string);
|
||||
protected abstract XhtmlNode addText(String cnt);
|
||||
protected abstract void addChildren(XhtmlNodeList childNodes);
|
||||
protected abstract int indexOfNode(XhtmlNode node);
|
||||
|
||||
public XhtmlNode h1() {
|
||||
return addTag("h1");
|
||||
@ -61,6 +63,14 @@ public abstract class XhtmlFluent {
|
||||
return addTag("tr");
|
||||
}
|
||||
|
||||
public XhtmlNode tr(XhtmlNode tr) {
|
||||
return addTag(indexOfNode(tr)+1, "tr");
|
||||
}
|
||||
|
||||
public XhtmlNode th(int index) {
|
||||
return addTag(index, "th");
|
||||
}
|
||||
|
||||
public XhtmlNode th() {
|
||||
return addTag("th");
|
||||
}
|
||||
@ -182,7 +192,7 @@ public abstract class XhtmlFluent {
|
||||
|
||||
public XhtmlNode img(String src, String alt) {
|
||||
if (alt == null) {
|
||||
return addTag("img").attribute("src", src);
|
||||
return addTag("img").attribute("src", src).attribute("alt", ".");
|
||||
} else {
|
||||
return addTag("img").attribute("src", src).attribute("alt", alt);
|
||||
}
|
||||
|
@ -203,19 +203,16 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
}
|
||||
}
|
||||
|
||||
public XhtmlNode addTag(String name)
|
||||
{
|
||||
|
||||
private XhtmlNode makeTag(String name) {
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) {
|
||||
throw new Error("Wrong node type - node is "+nodeType.toString()+" ('"+getName()+"/"+getContent()+"')");
|
||||
}
|
||||
|
||||
// if (inPara && name.equals("p")) {
|
||||
// throw new FHIRException("nested Para");
|
||||
// }
|
||||
// if (inLink && name.equals("a")) {
|
||||
// throw new FHIRException("Nested Link");
|
||||
// }
|
||||
// if (inPara && name.equals("p")) {
|
||||
// throw new FHIRException("nested Para");
|
||||
//}
|
||||
//if (inLink && name.equals("a")) {
|
||||
// throw new FHIRException("Nested Link");
|
||||
//}
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Element);
|
||||
node.setName(name);
|
||||
if (getChildNodes().isInPara() || name.equals("p")) {
|
||||
@ -224,35 +221,26 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
if (getChildNodes().isInLink() || name.equals("a")) {
|
||||
node.getChildNodes().setInLink(true);
|
||||
}
|
||||
getChildNodes().add(node);
|
||||
if (Utilities.existsInList(name, "b", "big", "i", "small", "tt", "abbr", "acronym", "cite", "code", "dfn", "em", "kbd", "strong", "samp", "var", "a", "bdo", "br", "img", "map", "object", "q", "script", "span", "sub", "sup", " button", "input", "label", "select", "textarea")) {
|
||||
node.notPretty();
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addTag(String name) {
|
||||
XhtmlNode node = makeTag(name);
|
||||
getChildNodes().add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public XhtmlNode addTag(int index, String name)
|
||||
{
|
||||
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type. is "+nodeType.toString());
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Element);
|
||||
if (getChildNodes().isInPara() || name.equals("p")) {
|
||||
node.getChildNodes().setInPara(true);
|
||||
}
|
||||
if (getChildNodes().isInLink() || name.equals("a")) {
|
||||
node.getChildNodes().setInLink(true);
|
||||
}
|
||||
node.setName(name);
|
||||
public XhtmlNode addTag(int index, String name) {
|
||||
XhtmlNode node = makeTag(name);
|
||||
getChildNodes().add(index, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addComment(String content)
|
||||
{
|
||||
public XhtmlNode addComment(String content) {
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Comment);
|
||||
@ -261,8 +249,7 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addDocType(String content)
|
||||
{
|
||||
public XhtmlNode addDocType(String content) {
|
||||
if (!(nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
XhtmlNode node = new XhtmlNode(NodeType.DocType);
|
||||
@ -271,8 +258,7 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addInstruction(String content)
|
||||
{
|
||||
public XhtmlNode addInstruction(String content) {
|
||||
if (!(nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Instruction);
|
||||
@ -280,8 +266,8 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
getChildNodes().add(node);
|
||||
return node;
|
||||
}
|
||||
public XhtmlNode addText(String content)
|
||||
{
|
||||
|
||||
public XhtmlNode addText(String content) {
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
if (content != null) {
|
||||
@ -293,8 +279,7 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return null;
|
||||
}
|
||||
|
||||
public XhtmlNode addText(int index, String content)
|
||||
{
|
||||
public XhtmlNode addText(int index, String content) {
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
if (content == null)
|
||||
@ -306,8 +291,7 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return node;
|
||||
}
|
||||
|
||||
public boolean allChildrenAreText()
|
||||
{
|
||||
public boolean allChildrenAreText() {
|
||||
boolean res = true;
|
||||
if (hasChildren()) {
|
||||
for (XhtmlNode n : childNodes)
|
||||
@ -376,6 +360,17 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return this;
|
||||
}
|
||||
|
||||
public XhtmlNode attributeNN(String name, String value) {
|
||||
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
|
||||
throw new Error("Wrong node type");
|
||||
if (name == null)
|
||||
throw new Error("name is null");
|
||||
if (value != null) {
|
||||
getAttributes().put(name, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasAttribute(String name) {
|
||||
return hasAttributes() && getAttributes().containsKey(name);
|
||||
}
|
||||
@ -781,12 +776,18 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
public XhtmlNode colspan(String n) {
|
||||
return setAttribute("colspan", n);
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode colspan(int n) {
|
||||
return setAttribute("colspan", Integer.toString(n));
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode rowspan(int n) {
|
||||
if (n > 1) {
|
||||
return setAttribute("rowspan", Integer.toString(n));
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addChildren(XhtmlNodeList childNodes) {
|
||||
@ -928,6 +929,15 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode td(int index) {
|
||||
XhtmlNode x = addTag(index, "td");
|
||||
XhtmlNode t = (XhtmlNode) getUserData("cells");
|
||||
if (t != null) {
|
||||
x.copyAllContent(t);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode td() {
|
||||
XhtmlNode x = addTag("td");
|
||||
XhtmlNode t = (XhtmlNode) getUserData("cells");
|
||||
@ -937,6 +947,27 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode td(int index, String width) {
|
||||
XhtmlNode x = addTag(index, "td");
|
||||
x.attribute("width", width);
|
||||
XhtmlNode t = (XhtmlNode) getUserData("cells");
|
||||
if (t != null) {
|
||||
x.copyAllContent(t);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode tdW(int width) {
|
||||
XhtmlNode x = addTag("td");
|
||||
x.attribute("width", Integer.toString(width));
|
||||
XhtmlNode t = (XhtmlNode) getUserData("cells");
|
||||
if (t != null) {
|
||||
x.copyAllContent(t);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// differs from tx because it returns the owner node, not the created text
|
||||
public XhtmlNode txN(String cnt) {
|
||||
@ -976,5 +1007,32 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode head() {
|
||||
return addTag("head");
|
||||
}
|
||||
|
||||
public XhtmlNode body() {
|
||||
return addTag("body");
|
||||
}
|
||||
public XhtmlNode title(String title) {
|
||||
return addTag("title").tx(title);
|
||||
}
|
||||
|
||||
public XhtmlNode link(String rel, String href) {
|
||||
return addTag("link").attribute("rel", rel).attribute("href", href);
|
||||
}
|
||||
|
||||
|
||||
public void wbr() {
|
||||
addTag("wbr");
|
||||
|
||||
}
|
||||
|
||||
protected int indexOfNode(XhtmlNode node) {
|
||||
return getChildNodes().indexOf(node);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -6,6 +6,8 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class XhtmlNodeList extends XhtmlFluent implements List<XhtmlNode>, java.io.Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -31,15 +33,13 @@ public class XhtmlNodeList extends XhtmlFluent implements List<XhtmlNode>, java.
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode addTag(String name)
|
||||
{
|
||||
|
||||
// if (inPara && name.equals("p")) {
|
||||
// throw new FHIRException("nested Para");
|
||||
// }
|
||||
// if (inLink && name.equals("a")) {
|
||||
// throw new FHIRException("Nested Link");
|
||||
// }
|
||||
private XhtmlNode makeTag(String name) {
|
||||
// if (inPara && name.equals("p")) {
|
||||
// throw new FHIRException("nested Para");
|
||||
//}
|
||||
//if (inLink && name.equals("a")) {
|
||||
// throw new FHIRException("Nested Link");
|
||||
//}
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Element);
|
||||
node.setName(name);
|
||||
if (isInPara() || name.equals("p")) {
|
||||
@ -48,10 +48,24 @@ public class XhtmlNodeList extends XhtmlFluent implements List<XhtmlNode>, java.
|
||||
if (isInLink() || name.equals("a")) {
|
||||
node.getChildNodes().setInLink(true);
|
||||
}
|
||||
if (Utilities.existsInList(name, "b", "big", "i", "small", "tt", "abbr", "acronym", "cite", "code", "dfn", "em", "kbd", "strong", "samp", "var", "a", "bdo", "br", "img", "map", "object", "q", "script", "span", "sub", "sup", " button", "input", "label", "select", "textarea")) {
|
||||
node.notPretty();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addTag(String name) {
|
||||
XhtmlNode node = makeTag(name);
|
||||
add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode addTag(int index, String name) {
|
||||
XhtmlNode node = makeTag(name);
|
||||
add(index, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public XhtmlNode addText(String content) {
|
||||
if (content != null) {
|
||||
XhtmlNode node = new XhtmlNode(NodeType.Text);
|
||||
@ -183,4 +197,9 @@ public class XhtmlNodeList extends XhtmlFluent implements List<XhtmlNode>, java.
|
||||
protected void addChildren(XhtmlNodeList childNodes) {
|
||||
this.addAll(childNodes);
|
||||
}
|
||||
|
||||
protected int indexOfNode(XhtmlNode node) {
|
||||
return indexOf(node);
|
||||
}
|
||||
|
||||
}
|
@ -54,7 +54,7 @@ Extension_EXT_Type = The Extension ''{0}'' definition allows for the types {1} b
|
||||
Extension_EXT_URL_Absolute = Extension.url must be an absolute URL
|
||||
Extension_EXT_Unknown = Unknown extension {0}
|
||||
Extension_EXT_Unknown_NotHere = The extension {0} could not be found so is not allowed here
|
||||
Extension_EXT_Url_NotFound = Extension.url is required
|
||||
Extension_EXT_Url_NotFound = Extension.url is required in order to identify, use and validate the extension
|
||||
Extension_EXT_Version_Internal = Extension url ''{0}'' evaluation state invalid
|
||||
Extension_EXT_Version_Invalid = Extension url ''{0}'' is not valid (invalid Version ''{1}'')
|
||||
Extension_EXT_Version_InvalidId = Extension url ''{0}'' is not valid (invalid Element id ''{1}'')
|
||||
@ -488,14 +488,14 @@ UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__ = Unable to handle system {0}
|
||||
Unable_to_handle_system__filter_with_property__ = Unable to handle system {0} filter with property = {1}, op = {2}
|
||||
Unable_to_resolve_system__value_set_has_include_with_no_system = Unable to resolve system - value set {0} include #{1} has no system
|
||||
UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE = The code system {1} referred to from value set {0} has a grammar, and the code might be valid in it
|
||||
Unable_to_resolve_system__value_set_has_include_with_unknown_system = Unable to resolve system - value set {0} include #{1} has system {2} which icould not be found, and the server returned error {3}
|
||||
Unable_to_resolve_system__value_set_has_include_with_filter = Unable to resolve system - value set {0} include #{1} has a filter on system {2}
|
||||
Unable_to_resolve_system__value_set_has_imports = Unable to resolve system - value set has imports
|
||||
Unable_to_resolve_system__value_set_has_multiple_matches = Unable to resolve system - value set expansion has multiple matches: {0}
|
||||
Unable_to_resolve_system__value_set_expansion_has_multiple_systems = Unable to resolve system - value set expansion has multiple systems
|
||||
Unable_to_resolve_system__value_set_has_no_includes_or_expansion = Unable to resolve system - value set {0} has no includes or expansion
|
||||
Unable_to_resolve_system__value_set_has_include_with_unknown_system = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': include #{2} has system {3} which could not be found, and the server returned error {4}
|
||||
Unable_to_resolve_system__value_set_has_include_with_filter = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': include #{2} has a filter on system {3}
|
||||
Unable_to_resolve_system__value_set_has_imports = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set has imports
|
||||
Unable_to_resolve_system__value_set_has_multiple_matches = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set expansion has multiple matches: {2}
|
||||
Unable_to_resolve_system__value_set_expansion_has_multiple_systems = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set expansion has multiple systems
|
||||
Unable_to_resolve_system__value_set_has_no_includes_or_expansion = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set has no includes or expansion
|
||||
Unable_to_resolve_system__no_value_set = Unable to resolve system - no value set
|
||||
Unable_to_resolve_system__value_set_has_no_matches = Unable to determine system - value set has no matches for code ''{0}''
|
||||
Unable_to_resolve_system__value_set_has_no_matches = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}''
|
||||
This_base_property_must_be_an_Array_not_ = This base property must be an Array, not {0}
|
||||
documentmsg = (document)
|
||||
xml_attr_value_invalid = The XML Attribute {0} has an invalid character
|
||||
@ -917,10 +917,10 @@ CONCEPTMAP_GROUP_TARGET_MISSING = No Target Code System, so the target codes can
|
||||
CONCEPTMAP_GROUP_TARGET_UNKNOWN = The Target Code System {0} is not fully defined and populated, and no targetScope is specified, so the target code checking will not be performed
|
||||
CONCEPTMAP_GROUP_SOURCE_CODE_INVALID = The source code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_CODE_INVALID_VS = The source code ''{0}'' is not valid in the value set {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' is not valid. Possible codes {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' for the code ''{2}'' is not valid. Possible displays: {1}
|
||||
CONCEPTMAP_GROUP_TARGET_CODE_INVALID = The target code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_TARGET_CODE_INVALID_VS = The target code ''{0}'' is not valid in the value set {1}
|
||||
CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = The target display ''{0}'' is not valid. Possible displays {1}
|
||||
CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = The target display ''{0}'' for the code ''{2}'' is not valid. Possible displays: {1}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID = The property code ''{0}'' is not known
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH = The type of this property should be {1} not {0}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM = Since no system has been provided, a plain code cannot be used
|
||||
@ -932,7 +932,7 @@ CONCEPTMAP_GROUP_TARGET_INCOMPLETE = Target Code System {0} doesn''t have all co
|
||||
SD_NO_TYPE_CODE_ON_CODE = Snapshot for {1} element {0} has type.code without a value
|
||||
UNKNOWN_CODESYSTEM = A definition for CodeSystem ''{0}'' could not be found, so the code cannot be validated
|
||||
UNKNOWN_CODESYSTEM_VERSION = A definition for CodeSystem ''{0}'' version ''{1}'' could not be found, so the code cannot be validated. Valid versions: {2}
|
||||
UNABLE_TO_INFER_CODESYSTEM = The System URI could not be determined for the code {0} in the ValueSet {1}
|
||||
UNABLE_TO_INFER_CODESYSTEM = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}''
|
||||
VALUESET_TOO_COSTLY = The value set ''{0}'' expansion has too many codes to display ({1})
|
||||
VALUESET_TOO_COSTLY_COUNT = The value set ''{0}'' expansion has {2} codes, which is too many to display ({1})
|
||||
VALUESET_TOO_COSTLY_TIME = The value set ''{0}'' expansion took too long to process (>{1}sec)
|
||||
@ -1059,7 +1059,7 @@ CONCEPTMAP_VS_TOO_MANY_CODES = The concept map has too many codes to validate ({
|
||||
CONCEPTMAP_VS_CONCEPT_CODE_UNKNOWN_SYSTEM = The code ''{1}'' comes from the system {0} which could not be found, so it''s not known whether it''s valid in the value set ''{2}''
|
||||
CONCEPTMAP_VS_INVALID_CONCEPT_CODE = The code ''{1}'' in the system {0} is not valid in the value set ''{2}''
|
||||
CONCEPTMAP_VS_INVALID_CONCEPT_CODE_VER = The code ''{2}'' in the system {0} version {1} is not valid in the value set ''{3}''
|
||||
VALUESET_INC_TOO_MANY_CODES = The value set include has too many codes to validate ({0})
|
||||
VALUESET_INC_TOO_MANY_CODES = The value set include has too many codes to validate ({0}), so each individual code has not been checked
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH = The {1} resource did not match any of the allowed profiles (Type {2}: {3})
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES = The {1} resource matched more than one of the allowed profiles ({3})
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did not math the profile {2} because: {3}
|
||||
@ -1106,3 +1106,14 @@ TX_GENERAL_CC_ERROR_MESSAGE = No valid coding was found for the value set ''{0}'
|
||||
Validation_VAL_Profile_Minimum_SLICE_one = Slice ''{3}'': a matching slice is required, but not found (from {1}). Note that other slices are allowed in addition to this required slice
|
||||
Validation_VAL_Profile_Minimum_SLICE_other = Slice ''{3}'': minimum required = {0}, but only found {7} (from {1})
|
||||
FHIRPATH_UNKNOWN_EXTENSION = Reference to an unknown extension - double check that the URL ''{0}'' is correct
|
||||
Type_Specific_Checks_DT_XHTML_Resolve = Hyperlink ''{0}'' at ''{1}'' for ''{2}''' does not resolve
|
||||
Type_Specific_Checks_DT_XHTML_Resolve_Img = Image source ''{0}'' at ''{1}'' does not resolve
|
||||
TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES = Hyperlink ''{0}'' at ''{1}'' for ''{2}''' resolves to multiple targets
|
||||
CONTAINED_ORPHAN_DOM3 = The contained resource ''{0}'' is not referenced to from elsewhere in the containing resource nor does it refer to the containing resource (dom-3)
|
||||
VALUESET_INCLUDE_CS_NOT_CS = The include system ''{0}'' is a reference to a contained resource, but the contained resource with that id is not a CodeSystem, it's a {1}
|
||||
VALUESET_INCLUDE_CS_NOT_FOUND = No matching contained code system found for system ''{0}''
|
||||
VALUESET_INCLUDE_CSVER_NOT_FOUND = No matching contained code system found for system ''{0}'' version ''{1}''
|
||||
VALUESET_INCLUDE_CS_MULTI_FOUND = Multiple matching contained code systems found for system ''{0}''
|
||||
VALUESET_INCLUDE_CSVER_MULTI_FOUND = Multiple matching contained code systems found for system ''{0}'' version ''{1}''
|
||||
CODE_CASE_DIFFERENCE = The code ''{0}'' differs from the correct code ''{1}'' by case. Although the code system ''{2}'' is case insensitive, implementers are strongly encouraged to use the correct case anyway
|
||||
SCT_NO_MRCM = Not validated against the Machine Readable Concept Model (MRCM)
|
||||
|
@ -59,6 +59,7 @@ import org.hl7.fhir.r5.model.Constants;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.UsageContext;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
@ -75,6 +76,7 @@ import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
@ -85,7 +87,7 @@ import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
|
||||
public class BaseValidator implements IValidationContextResourceLoader {
|
||||
|
||||
public class BooleanHolder {
|
||||
public static class BooleanHolder {
|
||||
private boolean value = true;
|
||||
|
||||
public BooleanHolder() {
|
||||
@ -173,6 +175,8 @@ public class BaseValidator implements IValidationContextResourceLoader {
|
||||
protected Set<String> statusWarnings = new HashSet<>();
|
||||
protected BestPracticeWarningLevel bpWarnings = BestPracticeWarningLevel.Warning;
|
||||
protected String sessionId = Utilities.makeUuidLC();
|
||||
protected List<UsageContext> usageContexts = new ArrayList<UsageContext>();
|
||||
protected ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5);
|
||||
|
||||
|
||||
public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager, boolean debug) {
|
||||
@ -204,6 +208,8 @@ public class BaseValidator implements IValidationContextResourceLoader {
|
||||
this.statusWarnings = parent.statusWarnings;
|
||||
this.bpWarnings = parent.bpWarnings;
|
||||
this.urlRegex = parent.urlRegex;
|
||||
this.usageContexts.addAll(parent.usageContexts);
|
||||
this.baseOptions = parent.baseOptions;
|
||||
}
|
||||
|
||||
private boolean doingLevel(IssueSeverity error) {
|
||||
@ -1599,4 +1605,20 @@ public class BaseValidator implements IValidationContextResourceLoader {
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<UsageContext> getUsageContexts() {
|
||||
return usageContexts;
|
||||
}
|
||||
|
||||
protected boolean hasUseContext(Coding use, Coding value) {
|
||||
for (UsageContext usage : usageContexts) {
|
||||
if (isContext(use, value, usage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isContext(Coding use, Coding value, UsageContext usage) {
|
||||
return usage.getValue() instanceof Coding && context.subsumes(baseOptions, usage.getCode(), use) && context.subsumes(baseOptions, (Coding) usage.getValue(), value);
|
||||
}
|
||||
}
|
@ -668,29 +668,21 @@ public class IgLoader implements IValidationEngineLoader {
|
||||
InputStream stream = null;
|
||||
if (explore) {
|
||||
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "package.tgz"), true);
|
||||
if (stream != null)
|
||||
return loadPackage(stream, Utilities.pathURL(src, "package.tgz"), false);
|
||||
// todo: these options are deprecated - remove once all IGs have been rebuilt post R4 technical correction
|
||||
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "igpack.zip"), true);
|
||||
if (stream != null)
|
||||
return readZip(stream);
|
||||
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
|
||||
if (stream != null)
|
||||
return readZip(stream);
|
||||
stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true);
|
||||
//// -----
|
||||
if (stream != null) {
|
||||
try {
|
||||
return loadPackage(stream, Utilities.pathURL(src, "package.tgz"), false);
|
||||
} catch (Exception e) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ok, having tried all that... now we'll just try to access it directly
|
||||
byte[] cnt;
|
||||
List<String> errors = new ArrayList<>();
|
||||
if (stream != null) {
|
||||
cnt = TextFile.streamToBytes(stream);
|
||||
} else {
|
||||
cnt = fetchFromUrlSpecific(src, "application/json", true, errors);
|
||||
if (cnt == null) {
|
||||
cnt = fetchFromUrlSpecific(src, "application/xml", true, errors);
|
||||
}
|
||||
cnt = fetchFromUrlSpecific(src, "application/json", true, errors);
|
||||
if (cnt == null) {
|
||||
cnt = fetchFromUrlSpecific(src, "application/xml", true, errors);
|
||||
}
|
||||
if (cnt == null) {
|
||||
throw new FHIRException("Unable to fetch content from " + src + " (" + errors.toString() + ")");
|
||||
@ -740,7 +732,8 @@ public class IgLoader implements IValidationEngineLoader {
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private Manager.FhirFormat checkFormat(byte[] cnt, String filename) {
|
||||
private Manager.FhirFormat checkFormat(byte[] cnt, String filename) throws IOException {
|
||||
String text = TextFile.bytesToString(cnt);
|
||||
System.out.println(" ..Detect format for " + filename);
|
||||
try {
|
||||
org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(cnt);
|
||||
|
@ -56,6 +56,8 @@ public class CliContext {
|
||||
private HtmlInMarkdownCheck htmlInMarkdownCheck = HtmlInMarkdownCheck.WARNING;
|
||||
@JsonProperty("allowDoubleQuotesInFHIRPath")
|
||||
private boolean allowDoubleQuotesInFHIRPath = false;
|
||||
@JsonProperty("disableDefaultResourceFetcher")
|
||||
private boolean disableDefaultResourceFetcher = false;
|
||||
@JsonProperty("checkIPSCodes")
|
||||
private boolean checkIPSCodes;
|
||||
@JsonProperty("langTransform")
|
||||
@ -328,6 +330,17 @@ public class CliContext {
|
||||
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
|
||||
}
|
||||
|
||||
@JsonProperty("disableDefaultResourceFetcher")
|
||||
public boolean isDisableDefaultResourceFetcher() {
|
||||
return disableDefaultResourceFetcher;
|
||||
}
|
||||
|
||||
@JsonProperty("disableDefaultResourceFetcher")
|
||||
public CliContext setDisableDefaultResourceFetcher(boolean disableDefaultResourceFetcher) {
|
||||
this.disableDefaultResourceFetcher = disableDefaultResourceFetcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("checkIPSCodes")
|
||||
public boolean isCheckIPSCodes() {
|
||||
return checkIPSCodes;
|
||||
|
@ -524,9 +524,11 @@ public class ValidationService {
|
||||
validationEngine.setForPublication(cliContext.isForPublication());
|
||||
validationEngine.setShowTimes(cliContext.isShowTimes());
|
||||
validationEngine.setAllowExampleUrls(cliContext.isAllowExampleUrls());
|
||||
StandAloneValidatorFetcher fetcher = new StandAloneValidatorFetcher(validationEngine.getPcm(), validationEngine.getContext(), validationEngine);
|
||||
validationEngine.setFetcher(fetcher);
|
||||
validationEngine.getContext().setLocator(fetcher);
|
||||
if (!cliContext.isDisableDefaultResourceFetcher()) {
|
||||
StandAloneValidatorFetcher fetcher = new StandAloneValidatorFetcher(validationEngine.getPcm(), validationEngine.getContext(), validationEngine);
|
||||
validationEngine.setFetcher(fetcher);
|
||||
validationEngine.getContext().setLocator(fetcher);
|
||||
}
|
||||
validationEngine.getBundleValidationRules().addAll(cliContext.getBundleValidationRules());
|
||||
validationEngine.setJurisdiction(CodeSystemUtilities.readCoding(cliContext.getJurisdiction()));
|
||||
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());
|
||||
|
@ -89,6 +89,7 @@ public class Params {
|
||||
public static final String SRC_LANG = "-src-lang";
|
||||
public static final String TGT_LANG = "-tgt-lang";
|
||||
public static final String ALLOW_DOUBLE_QUOTES = "-allow-double-quotes-in-fhirpath";
|
||||
public static final String DISABLE_DEFAULT_RESOURCE_FETCHER = "-disable-default-resource-fetcher";
|
||||
public static final String CHECK_IPS_CODES = "-check-ips-codes";
|
||||
public static final String BEST_PRACTICE = "-best-practice";
|
||||
|
||||
@ -270,6 +271,8 @@ public class Params {
|
||||
cliContext.setNoExtensibleBindingMessages(true);
|
||||
} else if (args[i].equals(ALLOW_DOUBLE_QUOTES)) {
|
||||
cliContext.setAllowDoubleQuotesInFHIRPath(true);
|
||||
} else if (args[i].equals(DISABLE_DEFAULT_RESOURCE_FETCHER)) {
|
||||
cliContext.setDisableDefaultResourceFetcher(true);
|
||||
} else if (args[i].equals(CHECK_IPS_CODES)) {
|
||||
cliContext.setCheckIPSCodes(true);
|
||||
} else if (args[i].equals(NO_UNICODE_BIDI_CONTROL_CHARS)) {
|
||||
|
@ -156,6 +156,7 @@ import org.hl7.fhir.r5.model.TimeType;
|
||||
import org.hl7.fhir.r5.model.Timing;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.hl7.fhir.r5.model.UrlType;
|
||||
import org.hl7.fhir.r5.model.UsageContext;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.r5.renderers.DataRenderer;
|
||||
@ -494,18 +495,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
Element e = new ObjectConverter(context).convert((Resource) item);
|
||||
setParents(e);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null,
|
||||
mode);
|
||||
mode, false, false);
|
||||
} catch (IOException e1) {
|
||||
throw new FHIRException(e1);
|
||||
}
|
||||
} else if (item instanceof Element) {
|
||||
Element e = (Element) item;
|
||||
if (e.getSpecial() == SpecialElement.CONTAINED) {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false);
|
||||
} else if (e.getSpecial() != null) {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false);
|
||||
} else {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false, false);
|
||||
}
|
||||
} else
|
||||
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
|
||||
@ -603,7 +604,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
private List<BundleValidationRule> bundleValidationRules = new ArrayList<>();
|
||||
private boolean validateValueSetCodesOnTxServer = true;
|
||||
private QuestionnaireMode questionnaireMode;
|
||||
private ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5);
|
||||
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
|
||||
private boolean logProgress;
|
||||
private CodingsObserver codingObserver;
|
||||
@ -1002,7 +1002,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
long t = System.nanoTime();
|
||||
NodeStack stack = new NodeStack(context, path, element, validationLanguage);
|
||||
if (profiles == null || profiles.isEmpty()) {
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition));
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition), false, false);
|
||||
} else {
|
||||
int i = 0;
|
||||
while (i < profiles.size()) {
|
||||
@ -1020,7 +1020,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
i++;
|
||||
}
|
||||
for (StructureDefinition defn : profiles) {
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile));
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false, false);
|
||||
}
|
||||
}
|
||||
if (hintAboutNonMustSupport) {
|
||||
@ -2319,10 +2319,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (!ok) {
|
||||
if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false,
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString());
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER));
|
||||
} else {
|
||||
rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false,
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString());
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
@ -2399,7 +2399,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
} else if (sd.getType().equals(resource.fhirType())) {
|
||||
List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>();
|
||||
ValidationMode mode = new ValidationMode(ValidationReason.Expression, ProfileSource.FromExpression);
|
||||
validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode);
|
||||
validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode, false, false);
|
||||
boolean ok = true;
|
||||
List<ValidationMessage> record = new ArrayList<>();
|
||||
for (ValidationMessage v : valerrors) {
|
||||
@ -2951,6 +2951,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
ok = checkInnerNames(errors, e, path, xhtml.getChildNodes(), false) && ok;
|
||||
ok = checkUrls(errors, e, path, xhtml.getChildNodes()) && ok;
|
||||
ok = checkIdRefs(errors, e, path, xhtml, resource) && ok;
|
||||
if (true) {
|
||||
ok = checkReferences(valContext, errors, e, path, "div", xhtml, resource) && ok;
|
||||
}
|
||||
if (true) {
|
||||
ok = checkImageSources(valContext, errors, e, path, "div", xhtml, resource) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3064,6 +3070,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
|
||||
public boolean validateReference(ValidationContext valContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, String url) {
|
||||
boolean ok = true;
|
||||
if (url.startsWith("#")) {
|
||||
valContext.getInternalRefs().add(url.substring(1));
|
||||
}
|
||||
// now, do we check the URI target?
|
||||
if (fetcher != null && !type.equals("uuid")) {
|
||||
boolean found;
|
||||
@ -3319,6 +3328,99 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkReferences(ValidationContext valContext, List<ValidationMessage> errors, Element e, String path, String xpath, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element & "a".equals(node.getName()) && node.getAttribute("href") != null) {
|
||||
String href = node.getAttribute("href");
|
||||
if (!Utilities.noString(href) && href.startsWith("#") && !href.equals("#")) {
|
||||
String ref = href.substring(1);
|
||||
valContext.getInternalRefs().add(ref);
|
||||
int count = countTargetMatches(resource, ref, true);
|
||||
if (count == 0) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, node.allText());
|
||||
} else if (count > 1) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, href, xpath, node.allText());
|
||||
}
|
||||
} else {
|
||||
// we can't validate at this point. Come back and revisit this some time in the future
|
||||
}
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
checkReferences(valContext, errors, e, path, xpath+"/"+child.getName(), child, resource);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
protected int countTargetMatches(Element element, String fragment, boolean checkBundle) {
|
||||
int count = 0;
|
||||
if (fragment.equals(element.getIdBase())) {
|
||||
count++;
|
||||
}
|
||||
if (element.getXhtml() != null) {
|
||||
count = count + countTargetMatches(element.getXhtml(), fragment);
|
||||
}
|
||||
if (element.hasChildren()) {
|
||||
for (Element child : element.getChildren()) {
|
||||
count = count + countTargetMatches(child, fragment, false);
|
||||
}
|
||||
}
|
||||
if (count == 0 && checkBundle) {
|
||||
Element e = element.getParentForValidator();
|
||||
while (e != null) {
|
||||
if (e.fhirType().equals("Bundle")) {
|
||||
return countTargetMatches(e, fragment, false);
|
||||
}
|
||||
e = e.getParentForValidator();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int countTargetMatches(XhtmlNode node, String fragment) {
|
||||
int count = 0;
|
||||
if (fragment.equals(node.getAttribute("id"))) {
|
||||
count++;
|
||||
}
|
||||
if ("a".equals(node.getName()) && fragment.equals(node.getAttribute("name"))) {
|
||||
count++;
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
count = count + countTargetMatches(child, fragment);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkImageSources(ValidationContext valContext, List<ValidationMessage> errors, Element e, String path, String xpath, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element & "img".equals(node.getName()) && node.getAttribute("src") != null) {
|
||||
String src = node.getAttribute("src");
|
||||
if (src.startsWith("#")) {
|
||||
String ref = src.substring(1);
|
||||
valContext.getInternalRefs().add(ref);
|
||||
int count = countFragmentMatches(resource, ref);
|
||||
if (count == 0) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE_IMG, src, xpath);
|
||||
} else if (count > 1) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, src, xpath);
|
||||
}
|
||||
} else {
|
||||
// we can't validate at this point. Come back and revisit this some time in the future
|
||||
}
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
checkImageSources(valContext, errors, e, path, path+"/"+child.getName(), child, resource);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkIdRefs(List<ValidationMessage> errors, Element e, String path, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element && node.getAttribute("idref") != null) {
|
||||
@ -3431,6 +3533,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
|
||||
// firstly, resolve the value set
|
||||
ElementDefinitionBindingComponent binding = elementContext.getBinding();
|
||||
|
||||
if (binding.hasValueSet()) {
|
||||
ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl(), profile);
|
||||
if (vs == null) {
|
||||
@ -3454,7 +3557,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
vr = checkCodeOnServer(stack, vs, value, options);
|
||||
}
|
||||
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE, binding.getValueSet()) && ok;
|
||||
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() != BindingStrength.REQUIRED, binding.getValueSet()) && ok;
|
||||
|
||||
timeTracker.tx(t, "vc "+value+"");
|
||||
if (binding.getStrength() == BindingStrength.REQUIRED) {
|
||||
@ -3764,6 +3867,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
ok = bh.ok() && ok;
|
||||
String refType;
|
||||
if (ref.startsWith("#")) {
|
||||
valContext.getInternalRefs().add(ref.substring(1));
|
||||
refType = "contained";
|
||||
} else {
|
||||
if (we == null) {
|
||||
@ -3889,7 +3993,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
for (StructureDefinition pr : profiles) {
|
||||
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
|
||||
validateResource(we.valContext(valContext, pr), profileErrors, we.getResource(), we.getFocus(), pr,
|
||||
IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice));
|
||||
IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice), true, false);
|
||||
if (!hasErrors(profileErrors)) {
|
||||
goodCount++;
|
||||
goodProfiles.put(pr, profileErrors);
|
||||
@ -5275,7 +5379,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
|
||||
// checkSpecials = we're only going to run these tests if we are actually validating this content (as opposed to we looked it up)
|
||||
private boolean start(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException {
|
||||
private boolean start(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean fromContained) throws FHIRException {
|
||||
boolean ok = !hasErrors(errors);
|
||||
|
||||
checkLang(resource, stack);
|
||||
@ -5295,7 +5399,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
resolveBundleReferences(element, new ArrayList<Element>());
|
||||
}
|
||||
ok = startInner(valContext, errors, resource, element, defn, stack, valContext.isCheckSpecials(), pct, mode) && ok;
|
||||
ok = startInner(valContext, errors, resource, element, defn, stack, valContext.isCheckSpecials(), pct, mode, fromContained) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
@ -5314,7 +5418,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (pctOwned) {
|
||||
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress);
|
||||
}
|
||||
ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok;
|
||||
ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency), fromContained) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
@ -5362,7 +5466,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (pctOwned) {
|
||||
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getUrl(), logProgress);
|
||||
}
|
||||
ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile)) && ok;
|
||||
ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.MetaProfile), fromContained) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
@ -5379,7 +5483,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (pctOwned) {
|
||||
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sdi.getUrl(), logProgress);
|
||||
}
|
||||
ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency)) && ok;
|
||||
ok = startInner(valContext, errors, resource, element, sdi, stack, false, pct, mode.withSource(ProfileSource.ProfileDependency), fromContained) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
@ -5406,7 +5510,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (pctOwned) {
|
||||
pct = new PercentageTracker(resource.countDescendents(), resource.fhirType(), sd.getVersionedUrl(), logProgress);
|
||||
}
|
||||
ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile)) && ok;
|
||||
ok = startInner(valContext, errors, resource, element, sd, stack, false, pct, mode.withSource(ProfileSource.GlobalProfile), fromContained) && ok;
|
||||
if (pctOwned) {
|
||||
pct.done();
|
||||
}
|
||||
@ -5507,7 +5611,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startInner(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
|
||||
public boolean startInner(ValidationContext valContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode, boolean fromContained) {
|
||||
// the first piece of business is to see if we've validated this resource against this profile before.
|
||||
// if we have (*or if we still are*), then we'll just return our existing errors
|
||||
boolean ok = true;
|
||||
@ -5537,13 +5641,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
ok = false;
|
||||
}
|
||||
if (checkSpecials) {
|
||||
ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode) && ok;
|
||||
ok = checkSpecials(valContext, errors, element, stack, checkSpecials, pct, mode, fromContained) && ok;
|
||||
ok = validateResourceRules(errors, element, stack) && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
public boolean checkSpecials(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) {
|
||||
public boolean checkSpecials(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode, boolean contained) {
|
||||
boolean ok = true;
|
||||
|
||||
long t = System.nanoTime();
|
||||
@ -5559,7 +5663,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
|
||||
if (isHL7Core(element) && !isExample()) {
|
||||
ok = checkPublisherConsistency(errors, element, stack) && ok;
|
||||
ok = checkPublisherConsistency(valContext, errors, element, stack, contained) && ok;
|
||||
}
|
||||
}
|
||||
if (element.getType().equals(BUNDLE)) {
|
||||
@ -5605,11 +5709,38 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPublisherConsistency(List<ValidationMessage> errors, Element element, NodeStack stack) {
|
||||
private boolean checkPublisherConsistency(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack, boolean contained) {
|
||||
|
||||
String pub = element.getNamedChildValue("publisher", false);
|
||||
Base wgT = element.getExtensionValue(ToolingExtensions.EXT_WORKGROUP);
|
||||
String wg = wgT == null ? null : wgT.primitiveValue();
|
||||
|
||||
if (contained && wg == null) {
|
||||
boolean ok = true;
|
||||
Element container = valContext.getRootResource();
|
||||
if (element.hasExtension(ToolingExtensions.EXT_WORKGROUP)) {
|
||||
// container already specified the HL7 WG, so we don't need to test
|
||||
// but we're still going to test pub if it exists
|
||||
if (pub != null) {
|
||||
wgT = container.getExtensionValue(ToolingExtensions.EXT_WORKGROUP);
|
||||
wg = wgT == null ? null : wgT.primitiveValue();
|
||||
HL7WorkGroup wgd = HL7WorkGroups.find(wg);
|
||||
if (wgd != null) {
|
||||
String rpub = "HL7 International / "+wgd.getName();
|
||||
ok = rpub.equals(pub);
|
||||
if (!ok && wgd.getName2() != null) {
|
||||
ok = ("HL7 International / "+wgd.getName2()).equals(pub);
|
||||
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH2, wg, rpub, "HL7 International / "+wgd.getName2(), pub);
|
||||
} else {
|
||||
warningOrError(pub.contains("/"), errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), ok, I18nConstants.VALIDATION_HL7_PUBLISHER_MISMATCH, wg, rpub, pub);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<String> urls = new ArrayList<>();
|
||||
for (Element c : element.getChildren("contact")) {
|
||||
for (Element t : c.getChildren("telecom")) {
|
||||
@ -5618,6 +5749,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wg != null, I18nConstants.VALIDATION_HL7_WG_NEEDED, ToolingExtensions.EXT_WORKGROUP)) {
|
||||
HL7WorkGroup wgd = HL7WorkGroups.find(wg);
|
||||
if (rule(errors, "2023-09-15", IssueType.BUSINESSRULE, element.line(), element.col(), stack.getLiteralPath(), wgd != null, I18nConstants.VALIDATION_HL7_WG_UNKNOWN, wg)) {
|
||||
@ -5635,7 +5767,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
Utilities.startsWithInList( wgd.getLink(), urls), I18nConstants.VALIDATION_HL7_WG_URL, wg, wgd.getLink());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5820,7 +5953,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
|
||||
checkSpecials(valContext, errors, element, stack, ok, pct, mode);
|
||||
checkSpecials(valContext, errors, element, stack, ok, pct, mode, true);
|
||||
|
||||
if (typeForResource.getProfile().size() == 1) {
|
||||
long t = System.nanoTime();
|
||||
@ -5829,7 +5962,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
|
||||
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special == null ? "??" : special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) {
|
||||
trackUsage(profile, valContext, element);
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
@ -5841,7 +5974,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
trackUsage(profile, valContext, element);
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
|
||||
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) {
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
@ -5862,7 +5995,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
trackUsage(profile, valContext, element);
|
||||
List<ValidationMessage> perrors = new ArrayList<>();
|
||||
errorsList.add(perrors);
|
||||
if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode)) {
|
||||
if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode, false, special == SpecialElement.CONTAINED)) {
|
||||
bm.append(u.asStringValue());
|
||||
matched++;
|
||||
}
|
||||
@ -6124,7 +6257,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
String stype = ei.getElement().fhirType();
|
||||
if (!stype.equals(type)) {
|
||||
if (stype == null || !stype.equals(type)) {
|
||||
if (checkDefn.isChoice()) {
|
||||
if (extensionUrl != null && !isAbsolute(extensionUrl)) {
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getVersionedUrl(), type, stype) && ok;
|
||||
@ -6551,7 +6684,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
boolean ok = true;
|
||||
// 3. report any definitions that have a cardinality problem
|
||||
for (ElementDefinition ed : childDefinitions.getList()) {
|
||||
if (ed.getRepresentation().isEmpty()) { // ignore xml attributes
|
||||
if (!ed.hasRepresentation(PropertyRepresentation.XHTML) && !ed.hasRepresentation(PropertyRepresentation.XMLTEXT)) { // xhtml.value is XMLText in <R3
|
||||
int count = 0;
|
||||
List<ElementDefinition> slices = null;
|
||||
if (ed.hasSlicing()) {
|
||||
@ -6939,8 +7072,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
return true;
|
||||
}
|
||||
boolean ok = true;
|
||||
if (debug) {
|
||||
System.out.println("inv "+inv.getKey()+" on "+path+" in "+resource.fhirType()+" {{ "+inv.getExpression()+" }}"+time());
|
||||
// we don't allow dom-3 to execute - it takes too long (and is wrong).
|
||||
// instead, we enforce it in code
|
||||
if ("dom-3".equals(inv.getKey())) {
|
||||
return true;
|
||||
}
|
||||
ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache");
|
||||
if (n == null) {
|
||||
@ -7014,7 +7149,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
* The actual base entry point for internal use (re-entrant)
|
||||
*/
|
||||
private boolean validateResource(ValidationContext valContext, List<ValidationMessage> errors, Element resource,
|
||||
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException {
|
||||
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean forReference, boolean fromContained) throws FHIRException {
|
||||
|
||||
boolean ok = true;
|
||||
// check here if we call validation policy here, and then change it to the new interface
|
||||
@ -7066,10 +7201,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
// validate
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), checkResourceName(defn, resourceName, element.getFormat()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE,
|
||||
defn.getType(), resourceName, defn.getVersionedUrl())) {
|
||||
ok = start(valContext, errors, element, element, defn, stack, pct, mode) && ok; // root is both definition and type
|
||||
ok = start(valContext, errors, element, element, defn, stack, pct, mode, fromContained) && ok; // root is both definition and type
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
if (!forReference) {
|
||||
// last step: check that all contained resources are referenced or reference #
|
||||
ok = checkContainedReferences(valContext, errors, element, stack) && ok;
|
||||
}
|
||||
}
|
||||
if (testMode && ok == hasErrors(errors)) {
|
||||
throw new Error("ok is wrong. ok = "+ok+", errors = "+errorIds(stack.getLiteralPath(), ok, errors));
|
||||
@ -7077,6 +7216,59 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkContainedReferences(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack) {
|
||||
boolean ok = true;
|
||||
Set<String> baseRefs = (Set<String>) element.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
List<Element> containedList = element.getChildrenByName("contained");
|
||||
if (!containedList.isEmpty()) {
|
||||
boolean allDone = true;
|
||||
for (Element contained : containedList) {
|
||||
allDone = allDone && contained.hasUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
}
|
||||
if (allDone) {
|
||||
// We collected all the internal references in sets on the resource and the contained resources
|
||||
int i = 0;
|
||||
for (Element contained : containedList) {
|
||||
ok = checkContainedReferences(errors, stack, ok, baseRefs, containedList, i, contained);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkContainedReferences(List<ValidationMessage> errors, NodeStack stack, boolean ok,
|
||||
Set<String> baseRefs, List<Element> containedList, int i, Element contained) {
|
||||
NodeStack n = stack.push(contained, i, null, null);
|
||||
boolean found = isReferencedFromBase(contained, baseRefs, containedList, new ArrayList<>());
|
||||
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, n, found, I18nConstants.CONTAINED_ORPHAN_DOM3, contained.getIdBase()) && ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean isReferencedFromBase(Element contained, Set<String> baseRefs, List<Element> containedList, List<Element> ignoreList) {
|
||||
String id = contained.getIdBase();
|
||||
if (baseRefs.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
Set<String> irefs = (Set<String>) contained.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
if (irefs.contains("")) {
|
||||
return true;
|
||||
}
|
||||
for (Element c : containedList) {
|
||||
if (c != contained && !ignoreList.contains(c)) { // ignore list is to prevent getting into an unterminated loop
|
||||
Set<String> refs = (Set<String>) c.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
List<Element> ignoreList2 = new ArrayList<Element>();
|
||||
ignoreList2.addAll(ignoreList);
|
||||
ignoreList2.add(c);
|
||||
if (refs != null && refs.contains(id) && isReferencedFromBase(c, baseRefs, containedList, ignoreList2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkResourceName(StructureDefinition defn, String resourceName, FhirFormat format) {
|
||||
if (resourceName.equals(defn.getType())) {
|
||||
return true;
|
||||
@ -7101,11 +7293,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
}
|
||||
String s = b.toString();
|
||||
if (debug) {
|
||||
System.out.println("OK = "+ok+" for "+path);
|
||||
System.out.println("Errs = "+errors.toString());
|
||||
System.out.println("Ids = "+s);
|
||||
}
|
||||
// if (debug) {
|
||||
// System.out.println("OK = "+ok+" for "+path);
|
||||
// System.out.println("Errs = "+errors.toString());
|
||||
// System.out.println("Ids = "+s);
|
||||
// }
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -7527,4 +7719,5 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -176,12 +176,12 @@ public class BundleValidator extends BaseValidator {
|
||||
res.addMessage(signpost(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl()));
|
||||
}
|
||||
stack.resetIds();
|
||||
ok = validator().startInner(hostContext, errors, res, res, defn, rstack, false, pct, mode) && ok;
|
||||
ok = validator().startInner(hostContext, errors, res, res, defn, rstack, false, pct, mode, false) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
// also, while we're here, check the specials, since this doesn't happen anywhere else
|
||||
((InstanceValidator) parent).checkSpecials(hostContext, errors, res, rstack, true, pct, mode);
|
||||
((InstanceValidator) parent).checkSpecials(hostContext, errors, res, rstack, true, pct, mode, true);
|
||||
}
|
||||
|
||||
// todo: check specials
|
||||
|
@ -11,10 +11,12 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
@ -78,7 +80,12 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
public boolean hasTargetVS() {
|
||||
return targetScope != null && targetScope.vs != null;
|
||||
}
|
||||
|
||||
public ValueSet getSourceVS() {
|
||||
return hasSourceVS() ? sourceScope.vs : null;
|
||||
}
|
||||
public ValueSet getTargetVS() {
|
||||
return hasTargetVS() ? targetScope.vs : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -214,11 +221,9 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
|
||||
Element e = grp.getNamedChild("source", false);
|
||||
if (warning(errors, "2023-03-05", IssueType.REQUIRED, grp.line(), grp.col(), stack.getLiteralPath(), e != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_MISSING)) {
|
||||
ctxt.source = readCSReference(e, grp.getNamedChild("sourceVersion", false));
|
||||
ctxt.source = readCSReference(e, grp.getNamedChild("sourceVersion", false), ctxt.getSourceVS());
|
||||
if (ctxt.source.cs != null) {
|
||||
if (ctxt.source.cs.getContent() == CodeSystemContentMode.NOTPRESENT) {
|
||||
ctxt.source.cs = null;
|
||||
} else if (!warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), isOkCodeSystem(ctxt.source.cs), I18nConstants.CONCEPTMAP_GROUP_SOURCE_INCOMPLETE, e.getValue(), ctxt.source.cs.getContent().toCode())) {
|
||||
if (!warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), isOkCodeSystem(ctxt.source.cs), I18nConstants.CONCEPTMAP_GROUP_SOURCE_INCOMPLETE, e.getValue(), ctxt.source.cs.getContent().toCode())) {
|
||||
ctxt.source.cs = null;
|
||||
}
|
||||
} else {
|
||||
@ -227,11 +232,9 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
}
|
||||
e = grp.getNamedChild("target", false);
|
||||
if (warning(errors, "2023-03-05", IssueType.REQUIRED, grp.line(), grp.col(), stack.getLiteralPath(), e != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_MISSING)) {
|
||||
ctxt.target = readCSReference(e, grp.getNamedChild("targetVersion", false));
|
||||
ctxt.target = readCSReference(e, grp.getNamedChild("targetVersion", false), ctxt.getTargetVS());
|
||||
if (ctxt.target.cs != null) {
|
||||
if (ctxt.target.cs.getContent() == CodeSystemContentMode.NOTPRESENT) {
|
||||
ctxt.target.cs = null;
|
||||
} else if (!warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), isOkCodeSystem(ctxt.target.cs), I18nConstants.CONCEPTMAP_GROUP_TARGET_INCOMPLETE, e.getValue(), ctxt.target.cs.getContent().toCode())) {
|
||||
if (!warning(errors, "2023-03-05", IssueType.NOTFOUND, grp.line(), grp.col(), stack.push(e, -1, null, null).getLiteralPath(), isOkCodeSystem(ctxt.target.cs), I18nConstants.CONCEPTMAP_GROUP_TARGET_INCOMPLETE, e.getValue(), ctxt.target.cs.getContent().toCode())) {
|
||||
ctxt.target.cs = null;
|
||||
}
|
||||
} else {
|
||||
@ -248,7 +251,7 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
return ok;
|
||||
}
|
||||
|
||||
private CSReference readCSReference(Element ref, Element version) {
|
||||
private CSReference readCSReference(Element ref, Element version, ValueSet vs) {
|
||||
CSReference res = new CSReference();
|
||||
res.url = ref.primitiveValue();
|
||||
if (version != null) {
|
||||
@ -256,13 +259,19 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
} else if (res.url.contains("|")) {
|
||||
res.version = res.url.substring(res.url.indexOf("|")+1);
|
||||
res.url = res.url.substring(0, res.url.indexOf("|"));
|
||||
} else if (vs != null && res.url != null) {
|
||||
for (ConceptSetComponent vsi : vs.getCompose().getInclude()) {
|
||||
if (res.url.equals(vsi.getSystem()) && vsi.hasVersion() ) {
|
||||
res.version = vsi.getVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
res.cs = context.fetchCodeSystem(res.url, res.version);
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean isOkCodeSystem(CodeSystem tgtCS) {
|
||||
return tgtCS.getContent() != CodeSystemContentMode.EXAMPLE && tgtCS.getContent() != CodeSystemContentMode.FRAGMENT;
|
||||
return tgtCS.getContent() != CodeSystemContentMode.NOTPRESENT && tgtCS.getContent() != CodeSystemContentMode.EXAMPLE && tgtCS.getContent() != CodeSystemContentMode.FRAGMENT;
|
||||
}
|
||||
|
||||
private boolean validateGroupElement(List<ValidationMessage> errors, Element src, NodeStack stack, Map<String, PropertyDefinition> props, Map<String, String> attribs, ValidationOptions options, GroupContext ctxt) {
|
||||
@ -277,7 +286,7 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
if (warningOrError(ctxt.source.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_CODE_INVALID, c, ctxt.source.cs.getVersionedUrl())) {
|
||||
Element display = src.getNamedChild("display", false);
|
||||
if (display != null) {
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.source.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID, display.getValue(), CodeSystemUtilities.getDisplays(ctxt.source.cs, cd));
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.source.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID, display.getValue(), CommaSeparatedStringBuilder.joinWrapped(", ", "'", "'", CodeSystemUtilities.getDisplays(ctxt.source.cs, cd)), ctxt.source.cs.getVersionedUrl()+"#"+cd.getCode());
|
||||
}
|
||||
if (ctxt.hasSourceVS() && ctxt.source != null) {
|
||||
ValidationResult vr = context.validateCode(options.withCheckValueSetOnly().withNoServer(), ctxt.source.url, ctxt.source.version, c, null, ctxt.sourceScope.vs);
|
||||
@ -314,11 +323,11 @@ public class ConceptMapValidator extends BaseValidator {
|
||||
if (warningOrError(ctxt.target.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_CODE_INVALID, c, ctxt.target.cs.getVersionedUrl())) {
|
||||
Element display = tgt.getNamedChild("display", false);
|
||||
if (display != null) {
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.target.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID, display.getValue(), CodeSystemUtilities.getDisplays(ctxt.target.cs, cd));
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.target.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID, display.getValue(), CommaSeparatedStringBuilder.joinWrapped(", ", "'", "'", CodeSystemUtilities.getDisplays(ctxt.target.cs, cd)), ctxt.target.cs.getVersionedUrl()+"#"+cd.getCode());
|
||||
}
|
||||
if (ctxt.hasTargetVS() && ctxt.target != null) {
|
||||
ValidationResult vr = context.validateCode(options.withCheckValueSetOnly().withNoServer(), ctxt.target.url, ctxt.target.version, c, null, ctxt.targetScope.vs);
|
||||
if (!warningOrError(ctxt.target.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-09-06", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), vr.isOk(), I18nConstants.CONCEPTMAP_GROUP_SOURCE_CODE_INVALID_VS, c, ctxt.targetScope.vs.getVersionedUrl())) {
|
||||
if (!warningOrError(ctxt.target.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-09-06", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), vr.isOk(), I18nConstants.CONCEPTMAP_GROUP_TARGET_CODE_INVALID_VS, c, ctxt.targetScope.vs.getVersionedUrl())) {
|
||||
ok = (ctxt.target.cs.getContent() != CodeSystemContentMode.COMPLETE) && ok;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class ObservationValidator extends BaseValidator {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "LOINC", codes, pct, mode) && ok;
|
||||
} else if (hasLoincCode(code, codes, "39156-5", "39156-5", "59574-4", "89270-3")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "LOINC", codes, pct, mode) && ok;
|
||||
} else if (hasLoincCode(code, codes, "85354-9", "96607-7", "35094-2", "8459-0", "85354-9", "76534-7", "96607-7", "55284-4", "8480-6")) {
|
||||
} else if (hasLoincCode(code, codes, "85354-9", "35094-2", "8459-0", "85354-9", "76534-7", "55284-4", "8480-6")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "LOINC", codes, pct, mode) && ok;
|
||||
|
||||
} else if (hasSctCode(code, codes, "46680005")) {
|
||||
@ -81,7 +81,7 @@ public class ObservationValidator extends BaseValidator {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "SNOMED CT", codes, pct, mode) && ok;
|
||||
} else if (hasSctCode(code, codes, "60621009")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "SNOMED CT", codes, pct, mode) && ok;
|
||||
} else if (hasSctCode(code, codes, "75367002", "251076008", "6797001", "163033001", "123820005", "163035008", "723232008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) {
|
||||
} else if (hasSctCode(code, codes, "75367002", "251076008", "163033001", "163035008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "SNOMED CT", codes, pct, mode) && ok;
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ public class ObservationValidator extends BaseValidator {
|
||||
if (sd == null) {
|
||||
return false;
|
||||
} else {
|
||||
return ((InstanceValidator) parent).startInner(valContext, errors, element, element, sd, stack, false, pct, mode);
|
||||
return ((InstanceValidator) parent).startInner(valContext, errors, element, element, sd, stack, false, pct, mode, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ public class ValueSetValidator extends BaseValidator {
|
||||
List<Element> composes = vs.getChildrenByName("compose");
|
||||
int cc = 0;
|
||||
for (Element compose : composes) {
|
||||
ok = validateValueSetCompose(errors, compose, stack.push(compose, composes.size() > 1 ? cc : -1, null, null), vs.getNamedChildValue("url", false), "retired".equals(vs.getNamedChildValue("url", false))) & ok;
|
||||
ok = validateValueSetCompose(errors, compose, stack.push(compose, composes.size() > 1 ? cc : -1, null, null), vs.getNamedChildValue("url", false), "retired".equals(vs.getNamedChildValue("url", false)), vs) & ok;
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
@ -98,24 +98,24 @@ public class ValueSetValidator extends BaseValidator {
|
||||
}
|
||||
|
||||
|
||||
private boolean validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack, String vsid, boolean retired) {
|
||||
private boolean validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack, String vsid, boolean retired, Element vsSrc) {
|
||||
boolean ok = true;
|
||||
List<Element> includes = compose.getChildrenByName("include");
|
||||
int ci = 0;
|
||||
for (Element include : includes) {
|
||||
ok = validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid, retired) && ok;
|
||||
ok = validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid, retired, vsSrc) && ok;
|
||||
ci++;
|
||||
}
|
||||
List<Element> excludes = compose.getChildrenByName("exclude");
|
||||
int ce = 0;
|
||||
for (Element exclude : excludes) {
|
||||
ok = validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid, retired) && ok;
|
||||
ok = validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid, retired, vsSrc) && ok;
|
||||
ce++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack, String vsid, boolean retired) {
|
||||
private boolean validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack, String vsid, boolean retired, Element vsSrc) {
|
||||
boolean ok = true;
|
||||
String system = include.getChildValue("system");
|
||||
String version = include.getChildValue("version");
|
||||
@ -141,6 +141,25 @@ public class ValueSetValidator extends BaseValidator {
|
||||
if (valuesets.size() > 1) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, stack.getLiteralPath(), false, I18nConstants.VALUESET_IMPORT_UNION_INTERSECTION);
|
||||
}
|
||||
if (system != null && system.startsWith("#")) {
|
||||
List<Element> cs = new ArrayList<>();
|
||||
for (Element contained : vsSrc.getChildrenByName("contained")) {
|
||||
if (("#"+contained.getIdBase()).equals(system)) {
|
||||
if (rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), "CodeSystem".equals(contained.fhirType()), I18nConstants.VALUESET_INCLUDE_CS_NOT_CS, system, contained.fhirType())) {
|
||||
if (version == null || version.equals(contained.getChildValue("version"))) {
|
||||
cs.add(contained);
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cs.isEmpty()) {
|
||||
ok = rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), false, version == null ? I18nConstants.VALUESET_INCLUDE_CS_NOT_FOUND : I18nConstants.VALUESET_INCLUDE_CSVER_NOT_FOUND, system, version) && ok;
|
||||
} else {
|
||||
ok = rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), cs.size() == 1, version == null ? I18nConstants.VALUESET_INCLUDE_CS_MULTI_FOUND : I18nConstants.VALUESET_INCLUDE_CSVER_MULTI_FOUND, system, version) && ok;
|
||||
}
|
||||
}
|
||||
List<Element> concepts = include.getChildrenByName("concept");
|
||||
List<Element> filters = include.getChildrenByName("filter");
|
||||
|
||||
|
@ -9,10 +9,16 @@ public class FHIRPathExpressionFixer {
|
||||
// this is a hack work around for past publication of wrong FHIRPath expressions
|
||||
|
||||
boolean r5 = VersionUtilities.isR5Ver(version);
|
||||
// if (r5) {
|
||||
// return expr;
|
||||
// }
|
||||
boolean r4 = VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version);
|
||||
|
||||
// see https://chat.fhir.org/#narrow/stream/196008-ig-publishing-requirements/topic/Operation.20Definition.20Parameters.20table
|
||||
if (r5 && "opd-3".equals(key)) {
|
||||
return "targetProfile.exists() implies (type = 'Reference' or type = 'canonical' or type.memberOf('http://hl7.org/fhir/ValueSet/all-resource-types'))";
|
||||
}
|
||||
if (r4 && "opd-3".equals(key)) {
|
||||
return "targetProfile.exists() implies (type = 'Reference' or type = 'canonical' or type.memberOf('http://hl7.org/fhir/ValueSet/resource-types'))";
|
||||
}
|
||||
|
||||
if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
|
||||
return "(probability.exists() and (probability is decimal)) implies ((probability as decimal) <= 100)";
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package org.hl7.fhir.validation.instance.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
@ -11,6 +13,8 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
public class ValidationContext {
|
||||
|
||||
public static final String INTERNAL_REFERENCES_NAME = "internal.references";
|
||||
|
||||
private Object appContext;
|
||||
|
||||
// the version we are currently validating for right now
|
||||
@ -27,6 +31,7 @@ public class ValidationContext {
|
||||
|
||||
private boolean checkSpecials = true;
|
||||
private Map<String, List<ValidationMessage>> sliceRecords;
|
||||
private Set<String> internalRefs;
|
||||
|
||||
public ValidationContext(Object appContext) {
|
||||
this.appContext = appContext;
|
||||
@ -36,12 +41,22 @@ public class ValidationContext {
|
||||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = element;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
|
||||
// no groupingResource (Bundle or Parameters)
|
||||
dump("creating");
|
||||
}
|
||||
|
||||
private Set<String> setupInternalRefs(Element element) {
|
||||
Set<String> res = (Set<String>) element.getUserData(INTERNAL_REFERENCES_NAME);
|
||||
if (res == null) {
|
||||
res = new HashSet<String>();
|
||||
element.setUserData(INTERNAL_REFERENCES_NAME, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void check() {
|
||||
if (!rootResource.hasParentForValidator()) {
|
||||
throw new Error("No parent on root resource");
|
||||
@ -52,6 +67,7 @@ public class ValidationContext {
|
||||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = root;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
// no groupingResource (Bundle or Parameters)
|
||||
dump("creating");
|
||||
@ -62,6 +78,7 @@ public class ValidationContext {
|
||||
this.resource = element;
|
||||
this.rootResource = root;
|
||||
this.groupingResource = groupingResource;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
dump("creating");
|
||||
}
|
||||
@ -137,6 +154,7 @@ public class ValidationContext {
|
||||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(element);
|
||||
res.dump("forContained");
|
||||
return res;
|
||||
}
|
||||
@ -148,6 +166,7 @@ public class ValidationContext {
|
||||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(element);
|
||||
res.dump("forEntry");
|
||||
return res;
|
||||
}
|
||||
@ -159,6 +178,7 @@ public class ValidationContext {
|
||||
res.profile = profile;
|
||||
res.version = version;
|
||||
res.groupingResource = groupingResource;
|
||||
res.internalRefs = internalRefs;
|
||||
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forProfile "+profile.getUrl());
|
||||
return res;
|
||||
@ -171,6 +191,7 @@ public class ValidationContext {
|
||||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.checkSpecials = false;
|
||||
res.internalRefs = setupInternalRefs(resource);
|
||||
res.dump("forLocalReference "+profile.getUrl());
|
||||
res.version = version;
|
||||
return res;
|
||||
@ -191,6 +212,7 @@ public class ValidationContext {
|
||||
res.groupingResource = null;
|
||||
res.checkSpecials = false;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(resource);
|
||||
res.dump("forRemoteReference "+profile.getUrl());
|
||||
return res;
|
||||
}
|
||||
@ -203,6 +225,7 @@ public class ValidationContext {
|
||||
res.profile = profile;
|
||||
res.checkSpecials = false;
|
||||
res.version = version;
|
||||
res.internalRefs = internalRefs;
|
||||
res.sliceRecords = new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forSlicing");
|
||||
return res;
|
||||
@ -217,5 +240,9 @@ public class ValidationContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<String> getInternalRefs() {
|
||||
return internalRefs;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -340,7 +340,12 @@ public class TerminologyServiceTests {
|
||||
res.addParameter("system", new UriType(system));
|
||||
}
|
||||
if (vm.getCode() != null) {
|
||||
res.addParameter("code", new CodeType(vm.getCode()));
|
||||
if (code != null && !code.equals(vm.getCode())) {
|
||||
res.addParameter("code", new CodeType(code));
|
||||
res.addParameter("normalized-code", new CodeType(vm.getCode()));
|
||||
} else {
|
||||
res.addParameter("code", new CodeType(vm.getCode()));
|
||||
}
|
||||
} else if (code != null) {
|
||||
res.addParameter("code", new CodeType(code));
|
||||
}
|
||||
|
@ -10,8 +10,10 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.endsWith;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ -236,17 +238,43 @@ class ValidationServiceTest {
|
||||
*/
|
||||
@Test
|
||||
public void buildValidationEngineTest() throws IOException, URISyntaxException {
|
||||
|
||||
final org.hl7.fhir.utilities.TimeTracker timeTracker = mock(org.hl7.fhir.utilities.TimeTracker.class);
|
||||
|
||||
final TimeTracker timeTracker = mock(TimeTracker.class);
|
||||
final SimpleWorkerContext workerContext = mock(SimpleWorkerContext.class);
|
||||
|
||||
final ValidationEngine validationEngine = mock(ValidationEngine.class);
|
||||
when(validationEngine.getContext()).thenReturn(workerContext);
|
||||
final ValidationEngine mockValidationEngine = mock(ValidationEngine.class);
|
||||
when(mockValidationEngine.getContext()).thenReturn(workerContext);
|
||||
|
||||
final ValidationEngine.ValidationEngineBuilder validationEngineBuilder = mock(ValidationEngine.ValidationEngineBuilder.class);;
|
||||
final ValidationEngine.ValidationEngineBuilder mockValidationEngineBuilder = mock(ValidationEngine.ValidationEngineBuilder.class);;
|
||||
final ValidationService validationService = createFakeValidationService(mockValidationEngineBuilder, mockValidationEngine);
|
||||
|
||||
final ValidationService validationService = new ValidationService() {
|
||||
CliContext cliContext = new CliContext();
|
||||
validationService.buildValidationEngine(cliContext, null, timeTracker);
|
||||
|
||||
verify(mockValidationEngine).setFetcher(notNull());
|
||||
verify(mockValidationEngineBuilder).withUserAgent(eq("fhir/validator/" + VersionUtil.getVersion()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildValidationEngineDisableDefaultResourceFetcherTest() throws IOException, URISyntaxException {
|
||||
final TimeTracker timeTracker = mock(TimeTracker.class);
|
||||
final SimpleWorkerContext workerContext = mock(SimpleWorkerContext.class);
|
||||
|
||||
final ValidationEngine mockValidationEngine = mock(ValidationEngine.class);
|
||||
when(mockValidationEngine.getContext()).thenReturn(workerContext);
|
||||
|
||||
final ValidationEngine.ValidationEngineBuilder mockValidationEngineBuilder = mock(ValidationEngine.ValidationEngineBuilder.class);;
|
||||
final ValidationService validationService = createFakeValidationService(mockValidationEngineBuilder, mockValidationEngine);
|
||||
|
||||
CliContext cliContext = new CliContext();
|
||||
cliContext.setDisableDefaultResourceFetcher(true);
|
||||
validationService.buildValidationEngine(cliContext, null, timeTracker);
|
||||
|
||||
verify(mockValidationEngine, never()).setFetcher(any());
|
||||
verify(mockValidationEngineBuilder).withUserAgent(eq("fhir/validator/" + VersionUtil.getVersion()));
|
||||
}
|
||||
|
||||
private static ValidationService createFakeValidationService(ValidationEngine.ValidationEngineBuilder validationEngineBuilder, ValidationEngine validationEngine) {
|
||||
return new ValidationService() {
|
||||
@Override
|
||||
protected ValidationEngine.ValidationEngineBuilder getValidationEngineBuilder() {
|
||||
when(validationEngineBuilder.withTHO(anyBoolean())).thenReturn(validationEngineBuilder);
|
||||
@ -268,11 +296,5 @@ class ValidationServiceTest {
|
||||
//Don't care. Do nothing.
|
||||
}
|
||||
};
|
||||
|
||||
CliContext cliContext = new CliContext();
|
||||
|
||||
validationService.buildValidationEngine(cliContext, null, timeTracker);
|
||||
|
||||
verify(validationEngineBuilder).withUserAgent(eq("fhir/validator/" + VersionUtil.getVersion()));
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import org.hl7.fhir.r5.model.OperationOutcome;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.FhirPublication;
|
||||
import org.hl7.fhir.utilities.settings.FhirSettings;
|
||||
import org.hl7.fhir.utilities.tests.CacheVerificationLogger;
|
||||
@ -34,18 +35,7 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient-example.xml"), null);
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
if (!TestUtilities.silent) {
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" " + iss.getDetails().getText());
|
||||
}
|
||||
}
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(1, h);
|
||||
Assertions.assertTrue(checkOutcomes("test401Xml", op, "[] null information/informational: All OK"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
}
|
||||
|
||||
@ -57,15 +47,26 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "patient-example.json"), null);
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(1, h);
|
||||
Assertions.assertTrue(checkOutcomes("test401Json", op, "[] null information/informational: All OK"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
private boolean checkOutcomes(String id, OperationOutcome op, String text) {
|
||||
CommaSeparatedStringBuilder lines = new CommaSeparatedStringBuilder("\n");
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
lines.append(iss.toString());
|
||||
}
|
||||
String outcome = lines.toString();
|
||||
if (lines.toString().equals(text)) {
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("-- "+id+" -------");
|
||||
System.out.println("Expected:");
|
||||
System.out.println(text);
|
||||
System.out.println("Outcome:");
|
||||
System.out.println(outcome);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -76,18 +77,7 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient-example.xml"), null);
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
if (!TestUtilities.silent) {
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" " + iss.getDetails().getText());
|
||||
}
|
||||
}
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(1, h);
|
||||
Assertions.assertTrue(checkOutcomes("test430Xml", op, "[] null information/informational: All OK"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
}
|
||||
|
||||
@ -99,21 +89,8 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "patient-example.json"), null);
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
if (!TestUtilities.silent) {
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" " + iss.getDetails().getText());
|
||||
}
|
||||
}
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(1, h);
|
||||
Assertions.assertTrue(checkOutcomes("test430Json", op, "[] null information/informational: All OK"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -128,19 +105,8 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient140.xml"), null);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" " + iss.getDetails().getText());
|
||||
}
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(2, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(0, h);
|
||||
Assertions.assertTrue(checkOutcomes("test140", op, "Patient.contact[0].name.family[0].extension[0].value.ofType(code) null error/code-invalid: The value provided ('VV') was not found in the value set 'EntityNamePartQualifier' (http://hl7.org/fhir/ValueSet/name-part-qualifier|1.4.0), and a code is required from this value set (error message = The System URI could not be determined for the code 'VV' in the ValueSet 'http://hl7.org/fhir/ValueSet/name-part-qualifier|1.4.0'; The provided code '#VV' was not found in the value set 'http://hl7.org/fhir/ValueSet/name-part-qualifier|1.4.0')"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -156,19 +122,9 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient102.xml"), null);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" " + iss.getSeverity().toCode() + ": " + iss.getDetails().getText());
|
||||
}
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(2, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(0, h);
|
||||
Assertions.assertTrue(checkOutcomes("test102", op,
|
||||
"Patient.contact[0].name.family[0].extension[0].value.ofType(code) null error/code-invalid: The value provided ('VV') was not found in the value set 'EntityNamePartQualifier' (http://hl7.org/fhir/ValueSet/name-part-qualifier|1.0.2), and a code is required from this value set (error message = The System URI could not be determined for the code 'VV' in the ValueSet 'http://hl7.org/fhir/ValueSet/name-part-qualifier|1.0.2'; The provided code '#VV' was not found in the value set 'http://hl7.org/fhir/ValueSet/name-part-qualifier|1.0.2')"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -184,19 +140,13 @@ public class ValidationEngineTests {
|
||||
CacheVerificationLogger logger = new CacheVerificationLogger();
|
||||
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
|
||||
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation102.json"), null);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
|
||||
System.out.println(" "+iss.getSeverity().toCode()+": "+ iss.getDetails().getText());
|
||||
}
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(1, e);
|
||||
Assertions.assertEquals(2, w);
|
||||
Assertions.assertEquals(2, h);
|
||||
Assertions.assertTrue(checkOutcomes("testObs102", op,
|
||||
"Observation.text.div null error/invalid: Wrong namespace on the XHTML ('null', should be 'http://www.w3.org/1999/xhtml')\n"+
|
||||
"Observation.category null information/business-rule: Reference to experimental CodeSystem http://hl7.org/fhir/observation-category\n"+
|
||||
"Observation.code.coding[2].system null information/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated\n"+
|
||||
"Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer\n"+
|
||||
"Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have an effective[x] ()"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
|
||||
@ -210,16 +160,10 @@ public class ValidationEngineTests {
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. load USCore");
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "observation301.xml"), null);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent issue : op.getIssue())
|
||||
System.out.println(" - " + issue.getDetails().getText()+" ("+issue.getSeverity().toCode()+")");
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertTrue(checkOutcomes("test301", op,
|
||||
"Observation.code.coding[3].system null information/not-found: A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated\n"+
|
||||
"Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -236,18 +180,8 @@ public class ValidationEngineTests {
|
||||
List<String> profiles = new ArrayList<>();
|
||||
profiles.add("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient");
|
||||
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient301.xml"), profiles);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent issue : op.getIssue())
|
||||
System.out.println(" - " + issue.getDetails().getText());
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(1, e);
|
||||
Assertions.assertEquals(0, w);
|
||||
Assertions.assertEquals(0, h);
|
||||
Assertions.assertTrue(checkOutcomes("test301USCore", op, "Patient.name[1] null error/structure: Patient.name.family: minimum required = 1, but only found 0 (from http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|1.0.1)"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
|
||||
@ -263,47 +197,18 @@ public class ValidationEngineTests {
|
||||
igLoader.loadIg(ve.getIgs(), ve.getBinaries(), "hl7.fhir.us.core#3.1.1", false);
|
||||
List<String> profiles = new ArrayList<>();
|
||||
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation401_ucum.json"), profiles);
|
||||
if (!TestUtilities.silent)
|
||||
for (OperationOutcomeIssueComponent issue : op.getIssue())
|
||||
System.out.println(" - "+issue.getSeverity().toCode()+": " + issue.getDetails().getText());
|
||||
int e = errors(op);
|
||||
int w = warnings(op);
|
||||
int h = hints(op);
|
||||
Assertions.assertEquals(0, e);
|
||||
Assertions.assertEquals(6, w);
|
||||
Assertions.assertEquals(2, h);
|
||||
Assertions.assertTrue(checkOutcomes("test401USCore", op,
|
||||
"Observation null information/informational: Validate Observation against the Body weight profile (http://hl7.org/fhir/StructureDefinition/bodyweight) which is required by the FHIR specification because the LOINC code 29463-7 was found\n"+
|
||||
"Observation.code.coding[0].system null information/not-found: A definition for CodeSystem 'http://loinc.org' could not be found, so the code cannot be validated\n"+
|
||||
"Observation.value.ofType(Quantity) null warning/business-rule: Unable to validate code 'kg' in system 'http://unitsofmeasure.org' because the validator is running without terminology services\n"+
|
||||
"Observation.value.ofType(Quantity).code null warning/informational: Unable to validate code without using server because: Resolved system http://unitsofmeasure.org (v3.0.1), but the definition doesn't include any codes, so the code has not been validated\n"+
|
||||
"Observation.code null warning/code-invalid: None of the codings provided are in the value set 'Vital Signs' (http://hl7.org/fhir/ValueSet/observation-vitalsignresult|4.0.1), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://loinc.org#29463-7)\n"+
|
||||
"Observation null warning/invalid: Best Practice Recommendation: In general, all observations should have a performer\n"+
|
||||
"Observation.code null warning/not-found: Unable to check whether the code is in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult|4.0.1' because the code system http://loinc.org was not found\n"+
|
||||
"Observation null warning/invariant: Constraint failed: dom-6: 'A resource should have narrative for robust management' (defined in http://hl7.org/fhir/StructureDefinition/DomainResource) (Best Practice Recommendation)"));
|
||||
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
|
||||
if (!TestUtilities.silent)
|
||||
System.out.println(" .. done: " + Integer.toString(e) + " errors, " + Integer.toString(w) + " warnings, " + Integer.toString(h) + " information messages");
|
||||
}
|
||||
|
||||
|
||||
private int errors(OperationOutcome op) {
|
||||
int i = 0;
|
||||
for (OperationOutcomeIssueComponent vm : op.getIssue()) {
|
||||
if (vm.getSeverity() == IssueSeverity.ERROR || vm.getSeverity() == IssueSeverity.FATAL)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int warnings(OperationOutcome op) {
|
||||
int i = 0;
|
||||
for (OperationOutcomeIssueComponent vm : op.getIssue()) {
|
||||
if (vm.getSeverity() == IssueSeverity.WARNING)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
private int hints(OperationOutcome op) {
|
||||
int i = 0;
|
||||
for (OperationOutcomeIssueComponent vm : op.getIssue()) {
|
||||
if (vm.getSeverity() == IssueSeverity.INFORMATION || vm.getSeverity() == IssueSeverity.SUCCESS)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void execute() throws Exception {
|
||||
ValidationEngineTests self = new ValidationEngineTests();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,44 @@
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://acme.org/devices/clinical-codes",
|
||||
"code" : "body-weight",
|
||||
"display" : "Body Weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"code" : "body-weight",
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://local.fhir.org/r2",
|
||||
"unknown-systems" : "http://acme.org/devices/clinical-codes",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://local.fhir.org/r2"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-found"
|
||||
}],
|
||||
"text" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated"
|
||||
},
|
||||
"location" : ["Coding.system"]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://acme.org/devices/clinical-codes",
|
||||
"code" : "body-weight",
|
||||
@ -39,43 +79,3 @@ v: {
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://acme.org/devices/clinical-codes",
|
||||
"code" : "body-weight",
|
||||
"display" : "Body Weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"code" : "body-weight",
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r2",
|
||||
"unknown-systems" : "http://acme.org/devices/clinical-codes",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r2"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-found"
|
||||
}],
|
||||
"text" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated"
|
||||
},
|
||||
"location" : ["Coding.system"]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
|
@ -3,7 +3,7 @@
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "3141-9",
|
||||
"display" : "Weight Measured"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
@ -15,7 +15,7 @@ v: {
|
||||
"code" : "3141-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r2",
|
||||
"server" : "http://local.fhir.org/r2",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -26,7 +26,7 @@ v: {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "3141-9",
|
||||
"display" : "Weight Measured"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
|
@ -1,3 +1,4 @@
|
||||
[servers]
|
||||
local.fhir.org.r2 = http://local.fhir.org/r2
|
||||
tx-dev.fhir.org.r2 = http://tx-dev.fhir.org/r2
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"system" : "http://snomed.info/sct",
|
||||
"code" : "27113001",
|
||||
"display" : "Body weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
@ -15,7 +15,7 @@ v: {
|
||||
"code" : "27113001",
|
||||
"system" : "http://snomed.info/sct",
|
||||
"version" : "http://snomed.info/sct/900000000000207008/version/20230901",
|
||||
"server" : "http://tx-dev.fhir.org/r2",
|
||||
"server" : "http://local.fhir.org/r2",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -26,7 +26,7 @@ v: {
|
||||
"system" : "http://snomed.info/sct",
|
||||
"code" : "27113001",
|
||||
"display" : "Body weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
|
@ -2,19 +2,23 @@
|
||||
"systems" : [
|
||||
{
|
||||
"system" : "http://acme.org/devices/clinical-codes",
|
||||
"server" : "http://tx-dev.fhir.org/r2"
|
||||
"authoritative" : [],
|
||||
"candidates" : []
|
||||
},
|
||||
{
|
||||
"system" : "http://loinc.org",
|
||||
"server" : "http://tx-dev.fhir.org/r2"
|
||||
"authoritative" : [],
|
||||
"candidates" : []
|
||||
},
|
||||
{
|
||||
"system" : "http://snomed.info/sct",
|
||||
"server" : "http://tx-dev.fhir.org/r2"
|
||||
"authoritative" : [],
|
||||
"candidates" : []
|
||||
},
|
||||
{
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"server" : "http://tx-dev.fhir.org/r2"
|
||||
"authoritative" : [],
|
||||
"candidates" : []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
{"code" : {
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"code" : "[lb_av]"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
@ -14,7 +14,7 @@ v: {
|
||||
"code" : "[lb_av]",
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"version" : "2.0.1",
|
||||
"server" : "http://tx-dev.fhir.org/r2",
|
||||
"server" : "http://local.fhir.org/r2",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -24,7 +24,7 @@ v: {
|
||||
{"code" : {
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"code" : "[lb_av]"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1083,6 +1083,9 @@
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/slotstatus"
|
||||
},
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/spdx-license"
|
||||
},
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/special-values"
|
||||
},
|
||||
@ -3231,6 +3234,9 @@
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/hl7-work-group"
|
||||
},
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/icd-o-3"
|
||||
},
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/immunization-evaluation-dose-status"
|
||||
},
|
||||
|
@ -1,20 +0,0 @@
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "Foo"
|
||||
}, "url": "https://fhir.infoway-inforoute.ca/ValueSet/canadianjurisdiction", "version": "20170626", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"true", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"severity" : "error",
|
||||
"error" : "The code provided (Foo) is not in the expansion in the value set https://fhir.infoway-inforoute.ca/ValueSet/canadianjurisdiction, and a code is required from this value set. The system http://canadapost.ca/CodeSystem/ProvinceCodes could not be found.",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
@ -1,3 +1,4 @@
|
||||
[servers]
|
||||
local.fhir.org.r3 = http://local.fhir.org/r3
|
||||
tx-dev.fhir.org.r3 = http://tx-dev.fhir.org/r3
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1083,6 +1083,9 @@
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/slotstatus"
|
||||
},
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/spdx-license"
|
||||
},
|
||||
{
|
||||
"uri" : "http://hl7.org/fhir/special-values"
|
||||
},
|
||||
@ -3231,6 +3234,9 @@
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/hl7-work-group"
|
||||
},
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/icd-o-3"
|
||||
},
|
||||
{
|
||||
"uri" : "http://terminology.hl7.org/CodeSystem/immunization-evaluation-dose-status"
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ v: {
|
||||
"display" : "Finnish",
|
||||
"code" : "fi",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -34,7 +34,35 @@ v: {
|
||||
"code" : "d",
|
||||
"system" : "http://unitsofmeasure.org",
|
||||
"version" : "2.0.1",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "image/jpg"
|
||||
}, "valueSet" :{
|
||||
"resourceType" : "ValueSet",
|
||||
"compose" : {
|
||||
"include" : [{
|
||||
"system" : "urn:ietf:bcp:13"
|
||||
}]
|
||||
}
|
||||
}, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"true", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "image/jpg",
|
||||
"code" : "image/jpg",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -62,7 +90,35 @@ v: {
|
||||
"display" : "image/jpg",
|
||||
"code" : "image/jpg",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "application/pdf"
|
||||
}, "valueSet" :{
|
||||
"resourceType" : "ValueSet",
|
||||
"compose" : {
|
||||
"include" : [{
|
||||
"system" : "urn:ietf:bcp:13"
|
||||
}]
|
||||
}
|
||||
}, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"true", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "application/pdf",
|
||||
"code" : "application/pdf",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -90,7 +146,7 @@ v: {
|
||||
"display" : "application/pdf",
|
||||
"code" : "application/pdf",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -111,7 +167,7 @@ v: {
|
||||
"display" : "German (Switzerland)",
|
||||
"code" : "de-CH",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -134,15 +190,15 @@ v: {
|
||||
"code" : "urn:ihe:iti:xds:2017:mimeTypeSufficient",
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://ihe.net/fhir/ihe.formatcode.fhir/CodeSystem/formatcode' could not be found, so the code cannot be validated; The provided code 'http://ihe.net/fhir/ihe.formatcode.fhir/CodeSystem/formatcode#urn:ihe:iti:xds:2017:mimeTypeSufficient ('MimeType sufficient')' was not found in the value set 'http://hl7.org/fhir/ValueSet/formatcodes|20150326'",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://ihe.net/fhir/ihe.formatcode.fhir/CodeSystem/formatcode",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
@ -159,7 +215,7 @@ v: {
|
||||
{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "code-invalid",
|
||||
@ -193,7 +249,7 @@ v: {
|
||||
"code" : "US",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -214,7 +270,7 @@ v: {
|
||||
"display" : "English",
|
||||
"code" : "en",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://acme.org' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://acme.org",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -14,14 +14,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://acme.org/not-snomed' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://acme.org/not-snomed",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://example.org/system' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://example.org/system",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://mydomain.org/fhir/cs/mydomain' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://mydomain.org/fhir/cs/mydomain",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://myownsystem.info/sct' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://myownsystem.info/sct",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://spms.min-saude.pt/valueset-list-empty-reason' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://spms.min-saude.pt/valueset-list-empty-reason",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://spms.min-saude.pt/valueset-list' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "http://spms.min-saude.pt/valueset-list",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -15,14 +15,14 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'https://fhir.hl7.org.uk/STU3/CodeSystem/CareConnect-NHSNumberVerificationStatus-1' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "https://fhir.hl7.org.uk/STU3/CodeSystem/CareConnect-NHSNumberVerificationStatus-1",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
|
@ -14,7 +14,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -38,7 +38,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -62,7 +62,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -85,7 +85,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -108,7 +108,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -132,7 +132,7 @@ v: {
|
||||
"code" : "NL",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -156,7 +156,7 @@ v: {
|
||||
"code" : "US",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
@ -179,7 +179,7 @@ v: {
|
||||
"code" : "US",
|
||||
"system" : "urn:iso:std:iso:3166",
|
||||
"version" : "2018",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
|
@ -1,50 +1,4 @@
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "29463-7",
|
||||
"display" : "Body Weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Body weight",
|
||||
"code" : "29463-7",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "3141-9",
|
||||
"display" : "Body weight Measured"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Body weight Measured",
|
||||
"code" : "3141-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "19935-6",
|
||||
@ -61,8 +15,7 @@ v: {
|
||||
"code" : "19935-6",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -84,8 +37,7 @@ v: {
|
||||
"code" : "19935-6",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -108,8 +60,7 @@ v: {
|
||||
"code" : "19935-6",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -131,8 +82,7 @@ v: {
|
||||
"code" : "28655-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -154,8 +104,7 @@ v: {
|
||||
"code" : "28655-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -177,8 +126,7 @@ v: {
|
||||
"code" : "28655-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -201,8 +149,7 @@ v: {
|
||||
"code" : "29299-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -225,8 +172,7 @@ v: {
|
||||
"code" : "10183-2",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -249,8 +195,7 @@ v: {
|
||||
"code" : "48765-2",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -272,8 +217,7 @@ v: {
|
||||
"code" : "46241-6",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -296,8 +240,7 @@ v: {
|
||||
"code" : "18842-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -319,8 +262,7 @@ v: {
|
||||
"code" : "18842-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -343,8 +285,7 @@ v: {
|
||||
"code" : "18842-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -366,16 +307,15 @@ v: {
|
||||
"display" : "Allergies and adverse reactions Document",
|
||||
"code" : "48765-2",
|
||||
"severity" : "error",
|
||||
"error" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
|
||||
"error" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc' (en-US), '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "invalid",
|
||||
@ -384,7 +324,7 @@ v: {
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "invalid-display"
|
||||
}],
|
||||
"text" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
|
||||
"text" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc' (en-US), '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
|
||||
},
|
||||
"location" : ["CodeableConcept.coding[0].display"],
|
||||
"expression" : ["CodeableConcept.coding[0].display"]
|
||||
@ -409,8 +349,7 @@ v: {
|
||||
"code" : "8648-8",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -433,8 +372,7 @@ v: {
|
||||
"code" : "78375-3",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -457,8 +395,7 @@ v: {
|
||||
"code" : "75311-1",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -481,8 +418,7 @@ v: {
|
||||
"code" : "42347-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -505,8 +441,7 @@ v: {
|
||||
"code" : "42346-7",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -529,8 +464,7 @@ v: {
|
||||
"code" : "42344-2",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -553,8 +487,7 @@ v: {
|
||||
"code" : "10164-2",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -576,16 +509,15 @@ v: {
|
||||
"display" : "Plan of care note",
|
||||
"code" : "18776-5",
|
||||
"severity" : "error",
|
||||
"error" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')",
|
||||
"error" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note' (en-US), '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "invalid",
|
||||
@ -594,7 +526,7 @@ v: {
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "invalid-display"
|
||||
}],
|
||||
"text" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')"
|
||||
"text" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note' (en-US), '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')"
|
||||
},
|
||||
"location" : ["CodeableConcept.coding[0].display"],
|
||||
"expression" : ["CodeableConcept.coding[0].display"]
|
||||
@ -619,8 +551,7 @@ v: {
|
||||
"code" : "47420-5",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -643,8 +574,7 @@ v: {
|
||||
"code" : "47519-4",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
@ -666,16 +596,15 @@ v: {
|
||||
"display" : "Review of systems Narrative - Reported",
|
||||
"code" : "10187-3",
|
||||
"severity" : "error",
|
||||
"error" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
|
||||
"error" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems' (en-US), '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "invalid",
|
||||
@ -684,7 +613,7 @@ v: {
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "invalid-display"
|
||||
}],
|
||||
"text" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
|
||||
"text" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems' (en-US), '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
|
||||
},
|
||||
"location" : ["CodeableConcept.coding[0].display"],
|
||||
"expression" : ["CodeableConcept.coding[0].display"]
|
||||
@ -710,14 +639,13 @@ v: {
|
||||
"severity" : "error",
|
||||
"error" : "Wrong Display Name 'Administrative information' for http://loinc.org#87504-7. Valid display is 'LCDS v4.00 - Administrative information - discharge [CMS Assessment]' (for the language(s) '--')",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r3"
|
||||
"valueUrl" : "http://local.fhir.org/r3"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "invalid",
|
||||
@ -751,6 +679,53 @@ v: {
|
||||
"code" : "2069-3",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://local.fhir.org/r3",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "29463-7",
|
||||
"display" : "Body Weight"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Body weight",
|
||||
"code" : "29463-7",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "3141-9",
|
||||
"display" : "Body weight Measured"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Body weight Measured",
|
||||
"code" : "3141-9",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r3",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user