Squashed commit of the following:

commit df641506885e09e63f80efc6c658d01a1a3a8142
Author: James Agnew <jamesagnew@gmail.com>
Date:   Sat Sep 29 13:47:22 2018 -0400

    Shrink validation resources

commit 4c1550eafa26f4235b4b8804741aecc03276b5c6
Author: James Agnew <jamesagnew@gmail.com>
Date:   Fri Sep 28 22:45:28 2018 -0400

    Updates to get R4 working

commit 8332f15291006691ed9b07ead3e3524b0fc85510
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Fri Sep 28 09:02:13 2018 -0400

    Ongoing work on gettign R4 working

commit f7146cab7aed937bb625a8aec95744d76db00041
Author: James Agnew <jamesagnew@gmail.com>
Date:   Fri Sep 28 05:21:01 2018 -0400

    More work on sync

commit f48de4a10b1bab2584fee813017d9b27b237bfa9
Merge: 9e4f3cc722 aacb78b779
Author: James Agnew <jamesagnew@gmail.com>
Date:   Thu Sep 27 20:11:29 2018 -0400

    Merge branch 'sync_r4' of github.com:jamesagnew/hapi-fhir into sync_r4

commit 9e4f3cc722668aae31a77b19bf9b032af3ae01b0
Author: James Agnew <jamesagnew@gmail.com>
Date:   Thu Sep 27 20:11:19 2018 -0400

    Fix compile error

commit 7ec29e0ceda8f9013cbabb95767cbe15b4b81303
Merge: 23f7517325 002c4b3ff7
Author: James Agnew <jamesagnew@gmail.com>
Date:   Thu Sep 27 20:04:23 2018 -0400

    Merge branch 'master' into sync_r4

commit aacb78b7793ab2c71e0a2a774240ee2b5d3f9e7f
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Thu Sep 27 20:01:42 2018 -0400

    Keep working on getting R4 building

commit 4950de46d99897e74d41dfdb10d3d8a3435cb0c9
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Tue Sep 25 13:11:27 2018 -0400

    Ongoing work to get R4 working

commit 819d69c20e112dd3c72569ec50114ddb8263c300
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Thu Sep 20 08:15:36 2018 -0400

    Work on getting build working

commit 2c61b6cd1205ced5d7b822cf10942c69272ea078
Merge: 16b5bb06c8 dfb4de86f1
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Thu Sep 20 05:16:53 2018 -0400

    Merge branch 'master' into sync_r4

commit 16b5bb06c8cdaf8d67c3b80f5aa9be9fccd99aef
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Mon Sep 17 05:30:39 2018 -0400

    Work on R4 sync

commit 23f7517325a14dd2ca1eb5641296ead2776634ae
Author: James Agnew <jamesagnew@gmail.com>
Date:   Sun Sep 16 10:19:00 2018 -0400

    Work on sync

commit 6cc413c1f1dea538295aa4c16c21a5677494ac4c
Author: James Agnew <jamesagnew@gmail.com>
Date:   Fri Sep 14 17:28:28 2018 -0400

    Work on R$ sync

commit df6f6ad2ce783b07ccc383134705d874bc5d2cf1
Author: jamesagnew <jamesagnew@gmail.com>
Date:   Fri Sep 14 08:33:07 2018 -0400

    Work on R4
This commit is contained in:
James Agnew 2018-09-29 13:48:35 -04:00
parent 002c4b3ff7
commit 3e445faf47
2063 changed files with 1757374 additions and 1562001 deletions

View File

@ -133,7 +133,7 @@ public class ParserOptions {
} else if (thePaths instanceof HashSet) {
myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>) thePaths).clone();
} else {
myDontStripVersionsFromReferencesAtPaths = new HashSet<String>(thePaths);
myDontStripVersionsFromReferencesAtPaths = new HashSet<>(thePaths);
}
return this;
}

View File

@ -201,8 +201,10 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
* SPs with the same path the same way. This behaviour is
* used by AuthorizationInterceptor
*/
String nextPath = massagePathForCompartmentSimilarity(next.getPath());
for (RuntimeSearchParam nextAlternate : searchParams) {
if (nextAlternate.getPath().equals(next.getPath())) {
String nextAlternatePath = massagePathForCompartmentSimilarity(nextAlternate.getPath());
if (nextAlternatePath.equals(nextPath)) {
if (!nextAlternate.getName().equals(next.getName())) {
searchParamsForCompartment.add(nextAlternate);
}
@ -236,6 +238,14 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
}
private String massagePathForCompartmentSimilarity(String thePath) {
String path = thePath;
if (path.matches(".*\\.where\\(resolve\\(\\) is [a-zA-Z]+\\)")) {
path = path.substring(0, path.indexOf(".where"));
}
return path;
}
@Deprecated
public synchronized IBaseResource toProfile() {
validateSealed();

View File

@ -469,7 +469,7 @@ public abstract class BaseParser implements IParser {
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(theIResource);
if (shouldAddSubsettedTag()) {
tags = new TagList(tags);
tags.add(new Tag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE, subsetDescription()));
tags.add(new Tag(getSubsettedCodeSystem(), Constants.TAG_SUBSETTED_CODE, subsetDescription()));
}
return tags;
@ -746,7 +746,7 @@ public abstract class BaseParser implements IParser {
if (shouldAddSubsettedTag()) {
IBaseCoding coding = metaValue.addTag();
coding.setCode(Constants.TAG_SUBSETTED_CODE);
coding.setSystem(Constants.TAG_SUBSETTED_SYSTEM);
coding.setSystem(getSubsettedCodeSystem());
coding.setDisplay(subsetDescription());
}
@ -785,6 +785,14 @@ public abstract class BaseParser implements IParser {
return retVal;
}
private String getSubsettedCodeSystem() {
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
return Constants.TAG_SUBSETTED_SYSTEM_R4;
} else {
return Constants.TAG_SUBSETTED_SYSTEM_DSTU3;
}
}
@Override
public void setDontEncodeElements(Set<String> theDontEncodeElements) {
myDontEncodeElementsIncludesStars = false;

View File

@ -1286,9 +1286,7 @@ class ParserState<T> {
} else {
try {
myInstance.setValueAsString(theValue);
} catch (DataFormatException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage());
} catch (IllegalArgumentException e) {
} catch (DataFormatException | IllegalArgumentException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage());
}
}

View File

@ -182,7 +182,8 @@ public class Constants {
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String TAG_SUBSETTED_CODE = "SUBSETTED";
public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
public static final String TAG_SUBSETTED_SYSTEM_DSTU3 = "http://hl7.org/fhir/v3/ObservationValue";
public static final String TAG_SUBSETTED_SYSTEM_R4 = "http://terminology.hl7.org/CodeSystem/v3-ObservationValue";
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
public static final String OO_INFOSTATUS_PROCESSING = "processing";

View File

@ -89,7 +89,15 @@ public enum RestSearchParameterTypeEnum {
*/
HAS("string", "http://hl7.org/fhir/search-param-type"),
/**
* Code Value: <b>number</b>
*
* Search parameter SHALL be a number (a whole number, or a decimal).
*/
SPECIAL("special", "http://hl7.org/fhir/search-param-type"),
;
/**
* Identifier for this Value Set:

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.api.Constants;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.Arrays;
import java.util.List;
/**
*
*/
public class SpecialClientParam extends BaseClientParam implements IParam {
private final String myParamName;
public SpecialClientParam(String theParamName) {
myParamName = theParamName;
}
@Override
public String getParamName() {
return myParamName;
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.util.CoverageIgnore;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class SpecialAndListParam extends BaseAndListParam<SpecialOrListParam> {
@Override
SpecialOrListParam newInstance() {
return new SpecialOrListParam();
}
@CoverageIgnore
@Override
public SpecialAndListParam addAnd(SpecialOrListParam theValue) {
addValue(theValue);
return this;
}
}

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.util.CoverageIgnore;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class SpecialOrListParam extends BaseOrListParam<SpecialOrListParam, SpecialParam> {
@CoverageIgnore
@Override
SpecialParam newInstance() {
return new SpecialParam();
}
@CoverageIgnore
@Override
public SpecialOrListParam addOr(SpecialParam theParameter) {
add(theParameter);
return this;
}
}

View File

@ -0,0 +1,99 @@
package ca.uhn.fhir.rest.param;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.UriDt;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import static org.apache.commons.lang3.StringUtils.defaultString;
public class SpecialParam extends BaseParam /*implements IQueryParameterType*/ {
private String myValue;
/**
* Constructor
*/
public SpecialParam() {
super();
}
@Override
String doGetQueryParameterQualifier() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
String doGetValueAsQueryToken(FhirContext theContext) {
return ParameterUtil.escape(getValue());
}
/**
* {@inheritDoc}
*/
@Override
void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theParameter) {
setValue(ParameterUtil.unescape(theParameter));
}
/**
* Returns the value for the token (generally the value to the right of the
* vertical bar on the URL)
*/
public String getValue() {
return myValue;
}
public String getValueNotNull() {
return defaultString(myValue);
}
public boolean isEmpty() {
return StringUtils.isEmpty(myValue);
}
public SpecialParam setValue(String theValue) {
myValue = theValue;
return this;
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
builder.append("value", getValue());
if (getMissing() != null) {
builder.append(":missing", getMissing());
}
return builder.toString();
}
private static String toSystemValue(UriDt theSystem) {
return theSystem.getValueAsString();
}
}

View File

@ -160,7 +160,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
* The underlying cause exception
*/
public BaseServerResponseException(int theStatusCode, Throwable theCause) {
super(theCause.toString(), theCause);
super(theCause.getMessage(), theCause);
myStatusCode = theStatusCode;
myBaseOperationOutcome = null;
}

View File

@ -44,10 +44,23 @@ public class InvalidRequestException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_400_BAD_REQUEST;
private static final long serialVersionUID = 1L;
/**
* Constructor
*/
public InvalidRequestException(String theMessage) {
super(STATUS_CODE, theMessage);
}
/**
* Constructor
*/
public InvalidRequestException(String theMessage, Throwable theCause) {
super(STATUS_CODE, theMessage, theCause);
}
/**
* Constructor
*/
public InvalidRequestException(Throwable theCause) {
super(STATUS_CODE, theCause);
}

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.util;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
@ -24,6 +25,8 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
*/
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*;
@ -40,6 +43,7 @@ import ca.uhn.fhir.parser.DataFormatException;
public class FhirTerser {
public static final Pattern COMPARTMENT_MATCHER_PATH = Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
private FhirContext myContext;
public FhirTerser(FhirContext theContext) {
@ -364,8 +368,26 @@ public class FhirTerser {
List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName);
for (RuntimeSearchParam nextParam : params) {
for (String nextPath : nextParam.getPathsSplit()) {
/*
* DSTU3 and before just defined compartments as being (e.g.) named
* Patient with a path like CarePlan.subject
*
* R4 uses a fancier format like CarePlan.subject.where(resolve() is Patient)
*
* The following Regex is a hack to make that efficient at runtime.
*/
String wantType = null;
Pattern pattern = COMPARTMENT_MATCHER_PATH;
Matcher matcher = pattern.matcher(nextPath);
if (matcher.matches()) {
nextPath = matcher.group(1);
wantType = matcher.group(2);
}
for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) {
String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue();
IIdType nextTargetId = nextValue.getReferenceElement();
String nextRef = nextTargetId.toUnqualifiedVersionless().getValue();
/*
* If the reference isn't an explicit resource ID, but instead is just
@ -374,7 +396,7 @@ public class FhirTerser {
*/
if (isBlank(nextRef) && nextValue.getResource() != null) {
IBaseResource nextTarget = nextValue.getResource();
IIdType nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
if (!nextTargetId.hasResourceType()) {
String resourceType = myContext.getResourceDefinition(nextTarget).getName();
nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null);
@ -382,6 +404,12 @@ public class FhirTerser {
nextRef = nextTargetId.getValue();
}
if (isNotBlank(wantType)) {
if (!nextTargetId.getResourceType().equals(wantType)) {
continue;
}
}
if (wantRef.equals(nextRef)) {
return true;
}

View File

@ -28,7 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import java.util.Collections;
import java.util.List;
@ -39,7 +39,7 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
private FhirContext myCtx = FhirContext.forR4();
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}

View File

@ -98,9 +98,9 @@ public class VersionedApiConverterInterceptor extends InterceptorAdapter {
IBaseResource converted = null;
try {
if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) {
converted = myVersionConvertor_30_40.convertResource(toDstu3(responseResource));
converted = myVersionConvertor_30_40.convertResource(toDstu3(responseResource), true);
} else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) {
converted = myVersionConvertor_30_40.convertResource(toR4(responseResource));
converted = myVersionConvertor_30_40.convertResource(toR4(responseResource), true);
} else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) {
converted = myVersionConvertor_10_40.convertResource(toR4(responseResource));
} else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) {

View File

@ -1,28 +1,69 @@
package org.hl7.fhir.convertors;
/*-
* #%L
* HAPI FHIR - Converter
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class VersionConvertorConstants {
public final static String MODIFIER_REASON_EXTENSION = "http://hl7.org/fhir/tooling/StructureDefinition/r4ModifierReason";
public final static String MODIFIER_REASON_LEGACY = "No Modifier Reason provideed in previous versions of FHIR";
}
package org.hl7.fhir.convertors;
import org.hl7.fhir.utilities.Utilities;
public class VersionConvertorConstants {
public final static String IG_DEPENDSON_PACKAGE_EXTENSION = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ImplentationGuide.dependency.packageId";
public final static String IG_DEPENDSON_VERSION_EXTENSION = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ImplentationGuide.dependency.version";
public final static String MODIFIER_REASON_EXTENSION = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ElementDefinition.isModifierReason";
public final static String MODIFIER_REASON_LEGACY = "No Modifier Reason provideed in previous versions of FHIR";
public static String refToVS(String url) {
if (url == null)
return null;
if (url.equals("http://www.genenames.org"))
return "http://hl7.org/fhir/ValueSet/genenames";
else if (url.equals("http://varnomen.hgvs.org/"))
return "http://hl7.org/fhir/ValueSet/variants";
else if (url.equals("http://www.ncbi.nlm.nih.gov/nuccore?db=nuccore"))
return "http://hl7.org/fhir/ValueSet/ref-sequences";
else if (url.equals("http://www.ensembl.org/"))
return "http://hl7.org/fhir/ValueSet/ensembl";
else if (url.equals("http://www.ncbi.nlm.nih.gov/clinvar/variation"))
return "http://hl7.org/fhir/ValueSet/clinvar";
else if (url.equals("http://cancer.sanger.ac.uk/cancergenome/projects/cosmic/"))
return "http://hl7.org/fhir/ValueSet/cosmic";
else if (url.equals("http://www.ncbi.nlm.nih.gov/projects/SNP/"))
return "http://hl7.org/fhir/ValueSet/bbsnp";
else if (url.equals("http://www.sequenceontology.org/"))
return "http://hl7.org/fhir/ValueSet/sequenceontology";
else if (url.equals("http://www.ebi.ac.uk/"))
return "http://hl7.org/fhir/ValueSet/allelename";
else if (url.equals("https://www.iso.org/iso-4217-currency-codes.html"))
return "http://hl7.org/fhir/ValueSet/currencies";
else if (url.equals("http://www.rfc-editor.org/bcp/bcp13.txt"))
return "http://hl7.org/fhir/ValueSet/mimetypes";
else
return url;
}
public static String vsToRef(String url) {
if (url == null)
return null;
if (url.equals("http://hl7.org/fhir/ValueSet/genenames"))
return "http://www.genenames.org";
else if (url.equals("http://hl7.org/fhir/ValueSet/variants"))
return "http://varnomen.hgvs.org/";
else if (url.equals("http://hl7.org/fhir/ValueSet/ref-sequences"))
return "http://www.ncbi.nlm.nih.gov/nuccore?db=nuccore";
else if (url.equals("http://hl7.org/fhir/ValueSet/ensembl"))
return "http://www.ensembl.org/";
else if (url.equals("http://hl7.org/fhir/ValueSet/clinvar"))
return "http://www.ncbi.nlm.nih.gov/clinvar/variation";
else if (url.equals("http://hl7.org/fhir/ValueSet/cosmic"))
return "http://cancer.sanger.ac.uk/cancergenome/projects/cosmic/";
else if (url.equals("http://hl7.org/fhir/ValueSet/bbsnp"))
return "http://www.ncbi.nlm.nih.gov/projects/SNP/";
else if (url.equals("http://hl7.org/fhir/ValueSet/sequenceontology"))
return "http://www.sequenceontology.org/";
else if (url.equals("http://hl7.org/fhir/ValueSet/allelename"))
return "http://www.ebi.ac.uk/";
else if (url.equals("http://hl7.org/fhir/ValueSet/currencies"))
return "https://www.iso.org/iso-4217-currency-codes.html";
else if (url.equals("http://hl7.org/fhir/ValueSet/mimetypes"))
return "http://www.rfc-editor.org/bcp/bcp13.txt";
else
return null;
}
}

View File

@ -67,7 +67,7 @@ public class BaseR4Config extends BaseConfig {
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.what");
return retVal;
}

View File

@ -388,7 +388,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
ResourceTable resource = myResourceTableDao.findById(theResourceId).orElseThrow(IllegalStateException::new);
ResourceHistoryTable currentVersion = myResourceHistoryTableDao.findForIdAndVersion(resource.getId(), resource.getVersion());
expungeHistoricalVersion(currentVersion.getId());
if (currentVersion != null) {
expungeHistoricalVersion(currentVersion.getId());
}
ourLog.info("Deleting current version of resource {}", resource.getIdDt().getValue());
@ -2338,7 +2340,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
IResource res = (IResource) theResource;
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
if (tagList != null) {
tag = tagList.getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
tag = tagList.getTag(Constants.TAG_SUBSETTED_SYSTEM_DSTU3, Constants.TAG_SUBSETTED_CODE);
totalMetaCount += tagList.size();
}
List<IdDt> profileList = ResourceMetadataKeyEnum.PROFILES.get(res);
@ -2347,7 +2349,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
}
} else {
IAnyResource res = (IAnyResource) theResource;
tag = res.getMeta().getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
tag = res.getMeta().getTag(Constants.TAG_SUBSETTED_SYSTEM_DSTU3, Constants.TAG_SUBSETTED_CODE);
totalMetaCount += res.getMeta().getTag().size();
totalMetaCount += res.getMeta().getProfile().size();
totalMetaCount += res.getMeta().getSecurity().size();

View File

@ -41,6 +41,7 @@ import java.util.regex.Pattern;
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
public static final Pattern SPLIT = Pattern.compile("\\||( or )");
public static final Pattern SPLIT_R4 = Pattern.compile("\\|");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
@Autowired
private FhirContext myContext;

View File

@ -1186,6 +1186,10 @@ public class SearchBuilder implements ISearchBuilder {
BaseCodingDt id = (BaseCodingDt) theParameter;
system = id.getSystemElement().getValueAsString();
code = (id.getCodeElement().getValue());
} else if (theParameter instanceof NumberParam) {
NumberParam number = (NumberParam) theParameter;
system = null;
code = number.getValueAsQueryToken(myContext);
} else {
throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass());
}
@ -2082,11 +2086,16 @@ public class SearchBuilder implements ISearchBuilder {
* SearchParameters can declare paths on multiple resources
* types. Here we only want the ones that actually apply.
*/
for (Iterator<String> iter = path.iterator(); iter.hasNext(); ) {
if (!iter.next().startsWith(theResourceType + ".")) {
iter.remove();
path = new ArrayList<>(path);
for (int i = 0; i < path.size(); i++) {
String nextPath = trim(path.get(i));
if (!nextPath.contains(theResourceType + ".")) {
path.remove(i);
i--;
}
}
return theFrom.get("mySourcePath").in(path);
}

View File

@ -10,7 +10,7 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@ -66,7 +66,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
@Override
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
return null;
}

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao.r4;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -31,6 +31,7 @@ import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -48,14 +49,16 @@ import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR4.class);
@Autowired
private org.hl7.fhir.r4.hapi.ctx.IValidationSupport myValidationSupport;
@ -108,10 +111,11 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
ArrayList<PathAndRef> retVal = new ArrayList<>();
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
String[] nextPathsSplit = SPLIT_R4.split(theNextSpDef.getPath());
for (String path : nextPathsSplit) {
path = path.trim();
if (isNotBlank(path)) {
for (Object next : extractValues(path, theResource)) {
retVal.add(new PathAndRef(path, next));
}
@ -447,7 +451,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
}
} else if (nextObject instanceof Range) {
SimpleQuantity low = ((Range) nextObject).getLow();
Quantity low = ((Range) nextObject).getLow();
if (low != null) {
BigDecimal value = low.getValue();
if (value != null) {
@ -708,9 +712,10 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
IWorkerContext worker = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
fp.setHostServices(new SearchParamExtractorR4HostServices());
List<Object> values = new ArrayList<>();
String[] nextPathsSplit = SPLIT.split(thePaths);
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
for (String nextPath : nextPathsSplit) {
List<Base> allValues;
try {
@ -748,4 +753,88 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
return null;
}
private class SearchParamExtractorR4HostServices implements FHIRPathEngine.IEvaluationContext {
@Override
public Base resolveConstant(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public boolean log(String argument, List<Base> focus) {
return false;
}
@Override
public FunctionDetails resolveFunction(String functionName) {
return null;
}
@Override
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
return null;
}
@Override
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
return null;
}
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
/*
* When we're doing resolution within the SearchParamExtractor, if we want
* to do a resolve() it's just to check the type, so there is no point
* going through the heavyweight test. We can just return a stub and
* that's good enough since we're just doing something like
* Encounter.patient.where(resolve() is Patient)
*/
IdType url = new IdType(theUrl);
Base retVal = null;
if (isNotBlank(url.getResourceType())) {
retVal = myResourceTypeToStub.get(url.getResourceType());
if (retVal != null) {
return retVal;
}
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
if (resourceType != null) {
retVal = new Resource(){
@Override
public Resource copy() {
return this;
}
@Override
public ResourceType getResourceType() {
return resourceType;
}
@Override
public String fhirType() {
return url.getResourceType();
}
};
myResourceTypeToStub.put(url.getResourceType(), retVal);
}
}
return retVal;
}
@Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
return false;
}
}
}

View File

@ -83,11 +83,12 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
private Long myHashIdentity;
public ResourceIndexedSearchParamQuantity() {
// nothing
super();
}
public ResourceIndexedSearchParamQuantity(String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
this();
setParamName(theParamName);
setSystem(theSystem);
setValue(theValue);

View File

@ -39,6 +39,7 @@ import java.util.Date;
public class ResourceLink implements Serializable {
private static final long serialVersionUID = 1L;
public static final int SRC_PATH_LENGTH = 200;
@SequenceGenerator(name = "SEQ_RESLINK_ID", sequenceName = "SEQ_RESLINK_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESLINK_ID")
@ -46,7 +47,7 @@ public class ResourceLink implements Serializable {
@Column(name = "PID")
private Long myId;
@Column(name = "SRC_PATH", length = 100, nullable = false)
@Column(name = "SRC_PATH", length = SRC_PATH_LENGTH, nullable = false)
private String mySourcePath;
@ManyToOne(optional = false, fetch = FetchType.LAZY)

View File

@ -15,7 +15,7 @@ import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -142,10 +142,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSet valueSetToExpand = new ValueSet();
valueSetToExpand.getCompose().addInclude(theInclude);
return super.expandValueSet(valueSetToExpand).getExpansion();
ValueSet expanded = super.expandValueSet(valueSetToExpand);
return new ValueSetExpander.ValueSetExpansionOutcome(expanded);
}
@Override

View File

@ -385,7 +385,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
p.addName().addFamily("Hello");
TagList tl = new TagList();
tl.addTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
tl.addTag(Constants.TAG_SUBSETTED_SYSTEM_DSTU3, Constants.TAG_SUBSETTED_CODE);
ResourceMetadataKeyEnum.TAG_LIST.put(p, tl);
try {

View File

@ -653,7 +653,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().setFamily("Hello");
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM).setCode(Constants.TAG_SUBSETTED_CODE);
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM_DSTU3).setCode(Constants.TAG_SUBSETTED_CODE);
try {
myPatientDao.create(p, mySrd);

View File

@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;

View File

@ -136,6 +136,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired
@Qualifier("myImmunizationRecommendationDaoR4")
protected IFhirResourceDao<ImmunizationRecommendation> myImmunizationRecommendationDao;
@Autowired
@Qualifier("myRiskAssessmentDaoR4")
protected IFhirResourceDao<RiskAssessment> myRiskAssessmentDao;
protected IServerInterceptor myInterceptor;
@Autowired
@Qualifier("myLocationDaoR4")

View File

@ -382,7 +382,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
map.setLoadSynchronous(true);
map.add("reason", new ReferenceParam(conditionId));
List<String> results = toUnqualifiedVersionlessIdValues(myMedicationRequestDao.search(map));
assertThat(results, contains(mrId));
assertThat(results.toString(), results, contains(mrId));
}
/**

View File

@ -303,6 +303,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
* Per message from David Hay on Skype
*/
@Test
@Ignore
public void testEverythingWithLargeSet() throws Exception {
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
@ -583,40 +584,40 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testIndexNoDuplicatesNumber() {
final ImmunizationRecommendation res = new ImmunizationRecommendation();
res.addRecommendation().setDoseNumber(new PositiveIntType(1));
res.addRecommendation().setDoseNumber(new PositiveIntType(1));
res.addRecommendation().setDoseNumber(new PositiveIntType(1));
res.addRecommendation().setDoseNumber(new PositiveIntType(2));
res.addRecommendation().setDoseNumber(new PositiveIntType(2));
res.addRecommendation().setDoseNumber(new PositiveIntType(2));
final RiskAssessment res = new RiskAssessment();
res.addPrediction().setProbability(new DecimalType("1.0"));
res.addPrediction().setProbability(new DecimalType("1.0"));
res.addPrediction().setProbability(new DecimalType("1.0"));
res.addPrediction().setProbability(new DecimalType("2.0"));
res.addPrediction().setProbability(new DecimalType("2.0"));
res.addPrediction().setProbability(new DecimalType("2.0"));
res.addPrediction().setProbability(new DecimalType("2.0"));
IIdType id = myImmunizationRecommendationDao.create(res, mySrd).getId().toUnqualifiedVersionless();
IIdType id = myRiskAssessmentDao.create(res, mySrd).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_NUMBER, new NumberParam("1"))));
List<IIdType> actual = toUnqualifiedVersionlessIds(myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("1.0"))));
assertThat(actual, contains(id));
actual = toUnqualifiedVersionlessIds(myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("99.0"))));
assertThat(actual, empty());
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
ResourceTable resource = myResourceTableDao.findAll().iterator().next();
assertEquals("ImmunizationRecommendation", resource.getResourceType());
assertEquals("RiskAssessment", resource.getResourceType());
Class<ResourceIndexedSearchParamNumber> type = ResourceIndexedSearchParamNumber.class;
List<ResourceIndexedSearchParamNumber> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber(ImmunizationRecommendation.SP_DOSE_NUMBER, new BigDecimal("2.00"));
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber(RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00"));
expect0.setResource(resource);
expect0.calculateHashes();
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber(ImmunizationRecommendation.SP_DOSE_SEQUENCE, null);
expect1.setResource(resource).setMissing(true);
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber(RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00"));
expect1.setResource(resource);
expect1.calculateHashes();
ResourceIndexedSearchParamNumber expect2 = new ResourceIndexedSearchParamNumber(ImmunizationRecommendation.SP_DOSE_NUMBER, new BigDecimal("1.00"));
expect2.setResource(resource);
expect2.calculateHashes();
assertThat(results, containsInAnyOrder(expect0, expect1, expect2));
assertThat("Got: \"" + results.toString()+"\"", results, containsInAnyOrder(expect0, expect1));
}
});
}
@ -1041,9 +1042,29 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
}
@Test
public void testComponentQuantity() {
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(200));
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
String param = Observation.SP_COMPONENT_VALUE_QUANTITY;
{
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1");
SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(param, v1);
IBundleProvider result = myObservationDao.search(map);
assertThat("Got: "+ toUnqualifiedVersionlessIdValues(result), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue()));
}
}
@Test
public void testSearchCompositeParamQuantity() {
//@formatter:off
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
@ -1061,35 +1082,35 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code3")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
//@formatter:on
String param = Observation.SP_COMPONENT_CODE_VALUE_QUANTITY;
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val));
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue()));
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<>(v0, v1);
SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(param, val);
IBundleProvider result = myObservationDao.search(map);
assertThat("Got: "+ toUnqualifiedVersionlessIdValues(result), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<>(v0, v1);
IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val));
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue(), id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code4");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<>(v0, v1);
IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val));
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code4");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<>(v0, v1);
IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val));
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
@ -1543,26 +1564,26 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testSearchNumberParam() {
ImmunizationRecommendation e1 = new ImmunizationRecommendation();
RiskAssessment e1 = new RiskAssessment();
e1.addIdentifier().setSystem("foo").setValue("testSearchNumberParam01");
e1.addRecommendation().setDoseNumber(new PositiveIntType(4 * 24 * 60));
IIdType id1 = myImmunizationRecommendationDao.create(e1, mySrd).getId();
e1.addPrediction().setProbability(new DecimalType(4 * 24 * 60));
IIdType id1 = myRiskAssessmentDao.create(e1, mySrd).getId();
ImmunizationRecommendation e2 = new ImmunizationRecommendation();
RiskAssessment e2 = new RiskAssessment();
e2.addIdentifier().setSystem("foo").setValue("testSearchNumberParam02");
e2.addRecommendation().setDoseNumber(new PositiveIntType(4));
IIdType id2 = myImmunizationRecommendationDao.create(e2, mySrd).getId();
e2.addPrediction().setProbability(new DecimalType(4));
IIdType id2 = myRiskAssessmentDao.create(e2, mySrd).getId();
{
IBundleProvider found = myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_NUMBER, new NumberParam(">2")));
IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam(">2")));
assertEquals(2, found.size().intValue());
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1.toUnqualifiedVersionless(), id2.toUnqualifiedVersionless()));
}
{
IBundleProvider found = myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_NUMBER, new NumberParam("<1")));
IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("<1")));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_NUMBER, new NumberParam("4")));
IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("4")));
assertEquals(1, found.size().intValue());
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id2.toUnqualifiedVersionless()));
}
@ -2619,7 +2640,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
// Irrelevant include
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(Encounter.INCLUDE_EPISODEOFCARE);
params.addInclude(Encounter.INCLUDE_EPISODE_OF_CARE);
IBundleProvider search = myPatientDao.search(params);
List<IBaseResource> patients = toList(search);
assertEquals(1, patients.size());

View File

@ -35,6 +35,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
@SuppressWarnings("Duplicates")
public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
@ -204,10 +205,9 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) {
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome()));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
assertEquals(1, result.getMessages().size());
assertThat(result.getMessages().get(0).getMessage(), containsString("dom-6: A resource should have narrative for robust management"));
}
@Test
@ -1150,7 +1150,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeIn2 = new AuditEvent();
aeIn2.getType().setSystem("http://hl7.org/fhir/audit-event-type").setCode("rest");
aeIn2.getType().setSystem("http://terminology.hl7.org/CodeSystem/audit-event-type").setCode("rest");
IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeOut1 = new AuditEvent();
@ -1223,7 +1223,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
// Now let's update
valueSet = new ValueSet();
valueSet.setId(vsid);
valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A");
valueSet.getCompose().addInclude().setSystem("http://terminology.hl7.org/CodeSystem/v3-MaritalStatus").addConcept().setCode("A");
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless();

View File

@ -745,7 +745,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().setFamily("Hello");
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM).setCode(Constants.TAG_SUBSETTED_CODE);
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM_DSTU3).setCode(Constants.TAG_SUBSETTED_CODE);
try {
myPatientDao.create(p, mySrd);

View File

@ -721,7 +721,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
Observation obs = new Observation();
obs.setSubject(new Reference(ptid));
obs.setContext(new Reference(encid));
obs.setEncounter(new Reference(encid));
myObservationDao.create(obs);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {

View File

@ -131,7 +131,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
input.getMeta().getProfile().add(new CanonicalType(sd.getUrl()));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getContext().setReference("http://foo.com/Encounter/9");
input.getEncounter().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
@ -171,7 +171,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
input.getMeta().getProfile().add(new CanonicalType(profileUri));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getContext().setReference("http://foo.com/Encounter/9");
input.getEncounter().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");

View File

@ -228,15 +228,17 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
IPrimitiveType<String> display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/v2-0487");
StringType code = new StringType("BRN");
StringType system = new StringType("http://hl7.org/fhir/v2/0487");
StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/yesnodontknow");
StringType code = new StringType("Y");
StringType system = new StringType("http://terminology.hl7.org/CodeSystem/v2-0136");
ValidateCodeResult result = myValueSetDao.validateCode(vsIdentifier, null, code, system, display, coding, codeableConcept, mySrd);
ourLog.info(result.getMessage());
assertTrue(result.getMessage(), result.isResult());
assertEquals("Yes", result.getDisplay());
}
}

View File

@ -5,36 +5,42 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
import ca.uhn.fhir.jpa.dao.PathAndRef;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class SearchParamExtractorR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParamExtractorR4Test.class);
private static FhirContext ourCtx = FhirContext.forR4();
private static IValidationSupport ourValidationSupport;
private ISearchParamRegistry mySearchParamRegistry;
@Test
public void testParamWithOrInPath() {
Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
ISearchParamRegistry searchParamRegistry = new ISearchParamRegistry() {
@Before
public void before() {
mySearchParamRegistry = new ISearchParamRegistry() {
@Override
public void forceRefresh() {
// nothing
@ -42,7 +48,7 @@ public class SearchParamExtractorR4Test {
@Override
public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) {
throw new UnsupportedOperationException();
return getActiveSearchParams(theResourceName).get(theParamName);
}
@Override
@ -81,7 +87,14 @@ public class SearchParamExtractorR4Test {
}
};
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, searchParamRegistry);
}
@Test
public void testParamWithOrInPath() {
Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -90,6 +103,50 @@ public class SearchParamExtractorR4Test {
assertEquals("CODE", token.getValue());
}
@Test
public void testReferenceWithResolve() {
Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location/123"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Encounter", "location");
assertNotNull(param);
List<PathAndRef> links = extractor.extractResourceLinks(enc, param);
assertEquals(1, links.size());
assertEquals("Encounter.location.location.where(resolve() is Location)", links.get(0).getPath());
assertEquals("Location/123", ((Reference) links.get(0).getRef()).getReference());
}
@Test
public void testReferenceWithResolveMulti() {
Consent consent = new Consent();
consent.setSource(new Reference().setReference("Consent/999"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Consent", Consent.SP_SOURCE_REFERENCE);
assertNotNull(param);
List<PathAndRef> links = extractor.extractResourceLinks(consent, param);
assertEquals(1, links.size());
assertEquals("Consent.source.where(resolve() is Consent or resolve() is Contract or resolve() is QuestionnaireResponse or resolve() is DocumentReference)", links.get(0).getPath());
assertEquals("Consent/999", ((Reference) links.get(0).getRef()).getReference());
}
@Test
public void testExtractComponentQuantities() {
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(200));
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry);
Set<ResourceIndexedSearchParamQuantity> links = extractor.extractSearchParamQuantity(new ResourceTable(), o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(4, links.size());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -34,7 +34,7 @@ import java.nio.charset.StandardCharsets;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@SuppressWarnings("deprecation")

View File

@ -39,7 +39,7 @@ import java.nio.charset.StandardCharsets;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@TestPropertySource(properties = {

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings("Duplicates")
public class PatientEverythingR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientEverythingR4Test.class);

View File

@ -87,7 +87,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ACSN"))
.andParameter("system", new UriType("http://hl7.org/fhir/v2/0203"))
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203"))
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -245,7 +245,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("M"))
.andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus"))
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v3-MaritalStatus"))
.execute();
//@formatter:on

View File

@ -63,63 +63,20 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.AuditEvent;
import org.hl7.fhir.r4.model.BaseResource;
import org.hl7.fhir.r4.model.Basic;
import org.hl7.fhir.r4.model.Binary;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
import org.hl7.fhir.r4.model.CarePlan;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Device;
import org.hl7.fhir.r4.model.DocumentManifest;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Encounter.EncounterLocationComponent;
import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.ImagingStudy;
import org.hl7.fhir.r4.model.ImmunizationRecommendation;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.MedicationAdministration;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r4.model.QuestionnaireResponse;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ServiceRequest;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.r4.model.UnsignedIntType;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -169,6 +126,7 @@ import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
@SuppressWarnings("Duplicates")
public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class);
@ -547,7 +505,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
IIdType optId = ourClient.create().resource(options).execute().getId();
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions((optId.getValue()));
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setAnswerValueSet((optId.getValue()));
IIdType qId = ourClient.create().resource(q).execute().getId();
QuestionnaireResponse qa;
@ -747,7 +705,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
location.setPeriod(new Period().setStart(new Date(), TemporalPrecisionEnum.SECOND).setEnd(new Date(), TemporalPrecisionEnum.SECOND));
IIdType e1id = ourClient.create().resource(e1).execute().getId();
//@formatter:off
Bundle res = ourClient.search()
.forResource(Encounter.class)
.where(Encounter.IDENTIFIER.exactly().systemAndCode("urn:foo", "testDeepChainingE1"))
@ -755,7 +712,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.include(Location.INCLUDE_PARTOF.asRecursive())
.returnBundle(Bundle.class)
.execute();
//@formatter:on
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res));
assertEquals(3, res.getEntry().size());
assertEquals(1, genResourcesOfType(res, Encounter.class).size());
@ -763,6 +721,45 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
@Test
public void testReferenceOfWrongType() {
Group l2 = new Group();
l2.setActive(true);
IIdType l2id = ourClient.create().resource(l2).execute().getId();
Encounter e1 = new Encounter();
e1.getStatusElement().setValue(EncounterStatus.INPROGRESS);
e1.getSubject().setReference(l2id.toUnqualifiedVersionless().getValue());
IIdType e1id = ourClient.create().resource(e1).execute().getId();
// Wrong type
Bundle res = ourClient.search()
.forResource(Encounter.class)
.include(Encounter.INCLUDE_PATIENT.asRecursive())
.returnBundle(Bundle.class)
.execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res));
assertEquals(1, res.getEntry().size());
assertEquals(1, genResourcesOfType(res, Encounter.class).size());
assertEquals(e1id.toUnqualifiedVersionless(), genResourcesOfType(res, Encounter.class).get(0).getIdElement().toUnqualifiedVersionless());
// Right type
res = ourClient.search()
.forResource(Encounter.class)
.include(Encounter.INCLUDE_SUBJECT.asRecursive())
.returnBundle(Bundle.class)
.execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res));
assertEquals(2, res.getEntry().size());
assertEquals(1, genResourcesOfType(res, Encounter.class).size());
assertEquals(1, genResourcesOfType(res, Group.class).size());
}
@Test
public void testDeleteConditionalMultiple() {
String methodName = "testDeleteConditionalMultiple";
@ -830,16 +827,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
String methodName = "testDeleteConditionalNoMatches";
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?identifier=" + methodName);
CloseableHttpResponse resp = ourHttpClient.execute(delete);
try {
try (CloseableHttpResponse resp = ourHttpClient.execute(delete)) {
ourLog.info(resp.toString());
String response = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertEquals(200, resp.getStatusLine().getStatusCode());
assertThat(response, containsString(
"<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL &quot;Patient?identifier=testDeleteConditionalNoMatches&quot;. Deletion failed.\"/></issue>"));
} finally {
IOUtils.closeQuietly(resp);
}
}
@ -847,14 +841,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testDeleteInvalidReference() throws IOException {
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient");
CloseableHttpResponse response = ourHttpClient.execute(delete);
try {
try (CloseableHttpResponse response = ourHttpClient.execute(delete)) {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode());
assertThat(responseString, containsString("Can not perform delete, no ID provided"));
} finally {
response.close();
}
}
@ -1096,7 +1087,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
@ -1122,7 +1113,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Observation obs = new Observation();
obs.getSubject().setReferenceElement(patientId);
obs.getDevice().setReferenceElement(devId);
obs.getContext().setReferenceElement(encId);
obs.getEncounter().setReferenceElement(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
ourLog.info("IDs: EncU:" + encUId.getIdPart() + " Enc:" + encId.getIdPart() + " " + patientId.toUnqualifiedVersionless());
@ -1160,7 +1151,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
@ -1185,7 +1176,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Observation obs = new Observation();
obs.getSubject().setReferenceElement(patientId);
obs.getDevice().setReferenceElement(devId);
obs.getContext().setReferenceElement(encId);
obs.getEncounter().setReferenceElement(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onType(Encounter.class).named("everything").withNoParameters(Parameters.class).execute();
@ -1402,7 +1393,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device();
dev.setModel(methodName);
dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
@ -1562,6 +1553,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
* Per message from David Hay on Skype
*/
@Test
@Ignore
public void testEverythingWithLargeSet() throws Exception {
String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8);
@ -1975,6 +1967,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(id.withVersion("1").getValue(), history.getEntry().get(2).getResource().getId());
assertEquals(1, ((Patient) history.getEntry().get(2).getResource()).getName().size());
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(history));
try {
myBundleDao.validate(history, null, null, null, null, null, mySrd);
} catch (PreconditionFailedException e) {
@ -2537,7 +2531,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals("1", patientId.getVersionIdPart());
AuditEvent ae = new org.hl7.fhir.r4.model.AuditEvent();
ae.addEntity().getReference().setReference(patientId.toUnqualified().getValue());
ae.addEntity().getWhat().setReference(patientId.toUnqualified().getValue());
IIdType aeId = ourClient.create().resource(ae).execute().getId();
assertEquals("1", aeId.getVersionIdPart());
@ -2546,8 +2540,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertFalse(patient.getManagingOrganization().getReferenceElement().hasVersionIdPart());
ae = ourClient.read().resource(AuditEvent.class).withId(aeId).execute();
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasIdPart());
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasVersionIdPart());
assertTrue(ae.getEntityFirstRep().getWhat().getReferenceElement().hasIdPart());
assertTrue(ae.getEntityFirstRep().getWhat().getReferenceElement().hasVersionIdPart());
}
@ -3740,8 +3734,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try {
ourClient
.search()
.forResource(ImmunizationRecommendation.class)
.where(ImmunizationRecommendation.DOSE_NUMBER.withPrefix(ParamPrefixEnum.ENDS_BEFORE).number(100))
.forResource(Sequence.class)
.where(Sequence.END.withPrefix(ParamPrefixEnum.ENDS_BEFORE).number(100))
.prettyPrint()
.returnBundle(Bundle.class)
.execute();

View File

@ -114,7 +114,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem("http://hl7.org/fhir/v3/MaritalStatus");
include.setSystem("http://terminology.hl7.org/CodeSystem/v3-MaritalStatus");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
@ -505,9 +505,9 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("url", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.withParameter(Parameters.class, "code", new StringType("Y"))
.andParameter("url", new StringType("http://hl7.org/fhir/ValueSet/yesnodontknow"))
.andParameter("system", new StringType("http://terminology.hl7.org/CodeSystem/v2-0136"))
.useHttpGet()
.execute();
@ -521,7 +521,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("Yes", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
@AfterClass

View File

@ -101,7 +101,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
}
@Test
public void testUploadMissingPackage() throws Exception {
public void testUploadMissingPackage() {
//@formatter:off
try {
ourClient

View File

@ -42,10 +42,10 @@ import javax.persistence.EntityManager;
import java.util.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.*;
@SuppressWarnings({"unchecked"})

View File

@ -95,9 +95,9 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
.setPurpose("Monitor all admissions to Emergency")
.setTrigger(new TriggerDefinition()
.setType(TriggerDefinition.TriggerType.DATAADDED)
.setCondition(new TriggerDefinition.TriggerDefinitionConditionComponent()
.setCondition(new Expression()
.setDescription("Encounter Location = emergency (active/completed encounters, current or previous)")
.setLanguage(TriggerDefinition.ExpressionLanguage.TEXT_FHIRPATH)
.setLanguage(Expression.ExpressionLanguage.TEXT_FHIRPATH)
.setExpression("(this | %previous).location.where(location = 'Location/emergency' and status in {'active', 'completed'}).exists()")
)
);

View File

@ -2043,8 +2043,6 @@
<profile value="http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"/>
<!--CDA templateId: urn:hl7ii:2.16.840.1.113883.10.20.22.4.4:2015-08-01-->
</meta>
<clinicalStatus value="active"/>
<verificationStatus value="confirmed"/>
<category>
<coding>
<system value="http://snomed.info/sct"/>
@ -2116,8 +2114,6 @@
<profile value="http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"/>
<!--CDA templateId: urn:hl7ii:2.16.840.1.113883.10.20.22.4.4:2015-08-01-->
</meta>
<clinicalStatus value="active"/>
<verificationStatus value="confirmed"/>
<category>
<coding>
<system value="http://hl7.org/fhir/v3/ActCode"/>

View File

@ -348,17 +348,17 @@
<item>
<linkId value="1.1"/>
<type value="choice"/>
<options value="#verbal"/>
<answerValueSet value="#verbal"/>
</item>
<item>
<linkId value="1.2"/>
<type value="choice"/>
<options value="#motor"/>
<answerValueSet value="#motor"/>
</item>
<item>
<linkId value="1.3"/>
<type value="choice"/>
<options value="#eye"/>
<answerValueSet value="#eye"/>
</item>
</Questionnaire>
</resource>

View File

@ -11,7 +11,6 @@
},
"id": "cf-1505178568463",
"subject": {"reference": "Patient/2344"},
"clinicalStatus": "active",
"code": {"coding": [{
"system": "http://snomed.info/sct",
"code": "170631002",

View File

@ -69,7 +69,6 @@
}
}
],
"created" : "2005-12-24T09:35:00+11:00",
"status" : "current",
"description" : "Physical",
"context" : {

View File

@ -261,7 +261,7 @@
"subject": {
"reference": "urn:uuid:09a8d22c-1e23-4e5f-b3bf-505772f123d1"
},
"context": {
"encounter": {
"reference": "urn:uuid:29e3a213-0df4-41e0-88f8-e3be83277022"
},
"effectiveDateTime": "2011-12-01T09:49:00-05:00",
@ -289,7 +289,7 @@
"subject": {
"reference": "urn:uuid:09a8d22c-1e23-4e5f-b3bf-505772f123d1"
},
"context": {
"encounter": {
"reference": "urn:uuid:29e3a213-0df4-41e0-88f8-e3be83277022"
},
"effectiveDateTime": "2011-12-01T09:49:00-05:00",
@ -317,7 +317,7 @@
"subject": {
"reference": "urn:uuid:09a8d22c-1e23-4e5f-b3bf-505772f123d1"
},
"context": {
"encounter": {
"reference": "urn:uuid:29e3a213-0df4-41e0-88f8-e3be83277022"
},
"effectiveDateTime": "2011-12-01T09:49:00-05:00",
@ -345,7 +345,7 @@
"subject": {
"reference": "urn:uuid:09a8d22c-1e23-4e5f-b3bf-505772f123d1"
},
"context": {
"encounter": {
"reference": "urn:uuid:29e3a213-0df4-41e0-88f8-e3be83277022"
},
"effectiveDateTime": "2011-12-01T10:57:00-05:00",

View File

@ -131,7 +131,7 @@
"binding": {
"strength": "example",
"description": "Codes for identifying types of resources not yet defined by FHIR",
"valueSetCanonical": "http://hl7.org/fhir/ValueSet/basic-resource-type"
"valueSet": "http://hl7.org/fhir/ValueSet/basic-resource-type"
},
"mapping": [
{

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate;
*/
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableColumnTypeTask;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableTask;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -126,6 +127,11 @@ public class JdbcUtils {
return BaseTableColumnTypeTask.ColumnTypeEnum.STRING.getDescriptor(length);
case Types.BIGINT:
return BaseTableColumnTypeTask.ColumnTypeEnum.LONG.getDescriptor(null);
case Types.INTEGER:
return BaseTableColumnTypeTask.ColumnTypeEnum.INT.getDescriptor(null);
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
return BaseTableColumnTypeTask.ColumnTypeEnum.DATE_TIMESTAMP.getDescriptor(null);
default:
throw new IllegalArgumentException("Don't know how to handle datatype: " + dataType);
}

View File

@ -39,6 +39,13 @@ public abstract class BaseTableColumnTypeTask<T extends BaseTableTask> extends B
* Constructor
*/
BaseTableColumnTypeTask() {
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.DERBY_EMBEDDED, "integer");
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.MARIADB_10_1, "integer");
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.MYSQL_5_7, "integer");
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.MSSQL_2012, "int");
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.ORACLE_12C, "number(10,0)");
setColumnType(ColumnTypeEnum.INT, DriverTypeEnum.POSTGRES_9_4, "int4");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.DERBY_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MARIADB_10_1, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MYSQL_5_7, "bigint");
@ -147,8 +154,16 @@ public abstract class BaseTableColumnTypeTask<T extends BaseTableTask> extends B
Assert.isTrue(theColumnLength == null, "Must not supply a column length");
return "timestamp";
}
},
INT {
@Override
public String getDescriptor(Long theColumnLength) {
Assert.isTrue(theColumnLength == null, "Must not supply a column length");
return "int";
}
};
public abstract String getDescriptor(Long theColumnLength);
}

View File

@ -37,7 +37,22 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
* Constructor
*/
public HapiFhirJpaMigrationTasks() {
init340();
init350();
init360();
}
private void init360() {
Builder version = forVersion(VersionEnum.V3_6_0);
// Resource Link
Builder.BuilderWithTableName resourceLink = version.onTable("HFJ_RES_LINK");
version.startSectionWithMessage("Starting work on table: " + resourceLink.getTableName());
resourceLink
.modifyColumn("SRC_PATH")
.nonNullable()
.withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, ResourceLink.SRC_PATH_LENGTH);
}
private void init350() {
@ -425,5 +440,39 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.addSql(DriverTypeEnum.MSSQL_2012, "alter table TRM_CONCEPT_MAP_GRP_ELM_TGT add constraint FK_TCMGETARGET_ELEMENT foreign key (CONCEPT_MAP_GRP_ELM_PID) references TRM_CONCEPT_MAP_GRP_ELEMENT");
}
private void init340() {
Builder version = forVersion(VersionEnum.V3_4_0);
// CodeSystem Version
Builder.BuilderWithTableName resourceLink = version.onTable("TRM_CODESYSTEM_VER");
version.startSectionWithMessage("Starting work on table: " + resourceLink.getTableName());
resourceLink
.dropIndex("IDX_CSV_RESOURCEPID_AND_VER");
resourceLink
.dropColumn("RES_VERSION_ID");
resourceLink
.addColumn("CS_VERSION_ID")
.nullable()
.type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255);
resourceLink
.addColumn("CODESYSTEM_PID")
.nullable()
.type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
resourceLink
.addForeignKey("FK_CODESYSVER_CS_ID")
.toColumn("CODESYSTEM_PID")
.references("TRM_CODESYSTEM", "PID");
// Concept
Builder.BuilderWithTableName concept = version.onTable("TRM_CONCEPT");
version.startSectionWithMessage("Starting work on table: " + concept.getTableName());
concept
.addColumn("CODE_SEQUENCE")
.nullable()
.type(BaseTableColumnTypeTask.ColumnTypeEnum.INT);
}
}

View File

@ -6,6 +6,7 @@ import org.junit.Test;
import java.sql.SQLException;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class AddColumnTest extends BaseTest {
@ -26,6 +27,23 @@ public class AddColumnTest extends BaseTest {
assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "SOMETABLE"), containsInAnyOrder("PID", "TEXTCOL", "NEWCOL"));
}
@Test
public void testAddColumnInt() throws SQLException {
executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))");
AddColumnTask task = new AddColumnTask();
task.setTableName("SOMETABLE");
task.setColumnName("newcolint");
task.setColumnType(AddColumnTask.ColumnTypeEnum.INT);
task.setNullable(true);
getMigrator().addTask(task);
getMigrator().migrate();
String type = JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "newcolint");
assertEquals(BaseTableColumnTypeTask.ColumnTypeEnum.INT.getDescriptor(null), type);
}
@Test
public void testColumnAlreadyExists() throws SQLException {
executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), newcol bigint)");

View File

@ -59,6 +59,8 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
retVal.setFetchSizeDefaultMaximum(10000);
retVal.setReindexThreadCount(1);
retVal.setExpungeEnabled(true);
return retVal;
}

View File

@ -59,6 +59,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
retVal.setFetchSizeDefaultMaximum(10000);
retVal.setExpungeEnabled(true);
return retVal;
}

View File

@ -2,6 +2,7 @@ package ca.uhn.fhirtest.interceptor;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
@ -35,6 +36,9 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
return new RuleBuilder()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen()
.deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onServer().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().andThen()
.allowAll()
.build();
}

View File

@ -644,9 +644,11 @@ public class RestfulServerUtils {
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
if (fullId != null && fullId.hasVersionIdPart()) {
response.addHeader(Constants.HEADER_ETAG, "W/\"" + fullId.getVersionIdPart() + '"');
String versionIdPart = fullId.getVersionIdPart();
response.addHeader(Constants.HEADER_ETAG, createEtag(versionIdPart));
} else if (theResource != null && theResource.getMeta() != null && isNotBlank(theResource.getMeta().getVersionId())) {
response.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getMeta().getVersionId() + '"');
String versionId = theResource.getMeta().getVersionId();
response.addHeader(Constants.HEADER_ETAG, createEtag(versionId));
}
}
@ -739,6 +741,10 @@ public class RestfulServerUtils {
return response.sendWriterResponse(theStatusCode, contentType, charset, writer);
}
public static String createEtag(String theVersionId) {
return "W/\"" + theVersionId + '"';
}
public static Integer tryToExtractNamedParameter(RequestDetails theRequest, String theParamName) {
String[] retVal = theRequest.getParameters().get(theParamName);
if (retVal == null) {

View File

@ -11,7 +11,7 @@ import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.BundleUtil.BundleEntryParts;
import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -19,7 +19,6 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -376,7 +375,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
String compartmentOwnerResourceType = next.getResourceType();
if (!StringUtils.equals(appliesToResourceType, compartmentOwnerResourceType)) {
List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(compartmentOwnerResourceType);
if (params.isEmpty() == false) {
if (!params.isEmpty()) {
/*
* If this is a search, we can at least check whether

View File

@ -96,6 +96,12 @@ public class SearchParameter extends BaseQueryParameter {
ourParamTypes.put(HasParam.class, RestSearchParameterTypeEnum.HAS);
ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS);
ourParamTypes.put(HasAndListParam.class, RestSearchParameterTypeEnum.HAS);
ourParamTypes.put(SpecialParam.class, RestSearchParameterTypeEnum.SPECIAL);
ourParamTypes.put(SpecialOrListParam.class, RestSearchParameterTypeEnum.SPECIAL);
ourParamTypes.put(SpecialAndListParam.class, RestSearchParameterTypeEnum.SPECIAL);
ourParamQualifiers.put(RestSearchParameterTypeEnum.SPECIAL, CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING));
}
private List<Class<? extends IQueryParameterType>> myCompositeTypes = Collections.emptyList();

View File

@ -802,7 +802,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder(ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM, ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, stringContainsInOrder(ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3, ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -836,7 +836,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -856,7 +856,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"",
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"",
"\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));

View File

@ -1362,7 +1362,7 @@ public class XmlParserDstu2_1Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -1462,7 +1462,7 @@ public class XmlParserDstu2_1Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -1483,7 +1483,7 @@ public class XmlParserDstu2_1Test {
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"foo\"/>", "<code value=\"bar\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));

View File

@ -478,7 +478,7 @@ public class GenericClientDstu2_1Test {
client.read().resource(Patient.class).withId("1").execute();
fail();
} catch (FhirClientConnectionException e) {
assertEquals("java.lang.IllegalStateException", e.getMessage());
assertEquals(null, e.getMessage());
}
try {

View File

@ -808,7 +808,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, stringContainsInOrder(Constants.TAG_SUBSETTED_SYSTEM_DSTU3, Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -918,7 +918,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE + "\","));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE + "\","));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -940,7 +940,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",",
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",",
"\"code\": \"" + Constants.TAG_SUBSETTED_CODE + "\","));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));

View File

@ -22,7 +22,6 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Matchers;
import org.mockito.internal.stubbing.answers.ThrowsException;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.builder.Input;
@ -1544,7 +1543,7 @@ public class XmlParserDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -1658,7 +1657,7 @@ public class XmlParserDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -1681,7 +1680,7 @@ public class XmlParserDstu2Test {
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"foo\"/>", "<code value=\"bar\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>", "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));

View File

@ -195,7 +195,9 @@ public interface IWorkerContext {
* @throws FHIRException
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException;
StructureDefinition fetchTypeDefinition(String theCode);
public class ValidationResult {
private ConceptDefinitionComponent definition;
private IssueSeverity severity;

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.util.*;
@ -69,6 +70,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
vso = getExpander().expand(theSource, theProfile);
} catch (InvalidRequestException e) {
throw e;
} catch (TerminologyServiceException e) {
throw new InvalidRequestException(e.getMessage(), e);
} catch (Exception e) {
throw new InternalErrorException(e);
}
@ -89,6 +92,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
return myValidationSupport.expandValueSet(myCtx, theInc);
}
@Override
public StructureDefinition fetchTypeDefinition(String theCode) {
throw new UnsupportedOperationException();
}
@Override
public CodeSystem fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
@ -243,7 +251,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
}
return new ValidationResult(null, null);
return new ValidationResult(IssueSeverity.ERROR, null);
}
@Override

View File

@ -1,465 +1,461 @@
package org.hl7.fhir.dstu3.terminologies;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* Copyright (c) 2011+, HL7, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.ExpansionProfile;
import org.hl7.fhir.dstu3.model.Factory;
import org.hl7.fhir.dstu3.model.PrimitiveType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.dstu3.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.Utilities;
public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSetExpanderFactory factory;
private ValueSet focus;
private int maxExpansionSize = 500;
private int total;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super();
this.context = context;
this.factory = factory;
}
public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize;
}
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations,
ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
return null;
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system);
n.setCode(code);
if (isAbstract)
n.setAbstract(true);
if (inactive)
n.setInactive(true);
if (profile.getIncludeDesignations() && designations != null) {
for (ConceptDefinitionDesignationComponent t : designations) {
ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
}
}
ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null;
if (t == null)
n.setDisplay(display);
else
n.setDisplay(t.getValue());
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
}
return n;
}
private boolean filterContainsCode(List<ValueSet> filters, String system, String code) {
for (ValueSet vse : filters)
if (expansionContainsCode(vse.getExpansion().getContains(), system, code))
return true;
return false;
}
private boolean expansionContainsCode(List<ValueSetExpansionContainsComponent> contains, String system, String code) {
for (ValueSetExpansionContainsComponent cc : contains) {
if (system.equals(cc.getSystem()) && code.equals(cc.getCode()))
return true;
if (expansionContainsCode(cc.getContains(), system, code))
return true;
}
return false;
}
private ConceptDefinitionDesignationComponent getMatchingLang(List<ConceptDefinitionDesignationComponent> list, String lang) {
for (ConceptDefinitionDesignationComponent t : list)
if (t.getLanguage().equals(lang))
return t;
for (ConceptDefinitionDesignationComponent t : list)
if (t.getLanguage().startsWith(lang))
return t;
return null;
}
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
throws FHIRException {
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, np, profile, filters);
} else
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, null, profile, filters);
}
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly {
if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
copyImportContains(expand.getContains(), null, profile, filters);
}
private void excludeCode(String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem);
n.setCode(theCode);
String s = key(n);
excludeKeys.add(s);
}
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem());
}
if (exc.hasValueSet())
throw new Error("Processing Value set references in exclude is not yet done");
// importValueSet(imp.getValue(), params, profile);
CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
excludeCodes(context.expandVS(exc, false), params);
return;
}
for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode());
}
if (exc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(c.getSystem(), c.getCode());
}
}
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) {
if (profile == null)
profile = makeDefaultExpansion();
try {
focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID());
if (!profile.getUrl().startsWith("urn:uuid:"))
focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl()));
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
if (canBeHeirarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
focus.getExpansion().getContains().add(c);
}
} else {
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
}
}
}
if (total > 0) {
focus.getExpansion().setTotal(total);
}
return new ValueSetExpansionOutcome(focus);
} catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there?
throw e;
} catch (NoTerminologyServiceException e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE);
} catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
}
}
private ExpansionProfile makeDefaultExpansion() {
ExpansionProfile res = new ExpansionProfile();
res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase());
res.setExcludeNested(true);
res.setIncludeDesignations(false);
return res;
}
private void addToHeirarchy(List<ValueSetExpansionContainsComponent> target, List<ValueSetExpansionContainsComponent> source) {
for (ValueSetExpansionContainsComponent s : source) {
target.add(s);
}
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
return def.getDisplay();
}
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, FileNotFoundException, IOException, FHIRException {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, params, profile);
}
}
private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
if (value == null)
throw new TerminologyServiceException("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value);
if (vs == null)
throw new TerminologyServiceException("Unable to find imported value set " + value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
if (vso.getError() != null)
throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError());
if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set " + value);
if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
return vso.getValueset();
}
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) {
for (ValueSetExpansionContainsComponent c : list) {
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter);
copyImportContains(c.getContains(), np, profile, filter);
}
}
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
List<ValueSet> imports = new ArrayList<ValueSet>();
for (UriType imp : inc.getValueSet())
imports.add(importValueSet(imp.getValue(), params, profile));
if (!inc.hasSystem()) {
if (imports.isEmpty()) // though this is not supposed to be the case
return;
ValueSet base = imports.get(0);
imports.remove(0);
copyImportContains(base.getExpansion().getContains(), null, profile, imports);
} else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
addCodes(context.expandVS(inc, canBeHeirarchy), params, profile, imports);
return;
}
if (cs == null) {
if (context.isNoTerminologyServer())
throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString());
else
throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString());
}
if (cs.getContent() != CodeSystemContentMode.COMPLETE)
throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete");
if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion())));
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
}
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
}
if (inc.getFilter().size() == 1) {
ConceptSetFilterComponent fc = inc.getFilter().get(0);
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
canBeHeirarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports);
}
}
}
} else
throw new NotImplementedException("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
}
}
private List<ConceptDefinitionDesignationComponent> convertDesignations(List<ConceptReferenceDesignationComponent> list) {
List<ConceptDefinitionDesignationComponent> res = new ArrayList<CodeSystem.ConceptDefinitionDesignationComponent>();
for (ConceptReferenceDesignationComponent t : list) {
ConceptDefinitionDesignationComponent c = new ConceptDefinitionDesignationComponent();
c.setLanguage(t.getLanguage());
c.setUse(t.getUse());
c.setValue(t.getValue());
}
return res;
}
private String key(String uri, String code) {
return "{" + uri + "}" + code;
}
private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode());
}
}
package org.hl7.fhir.dstu3.terminologies;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* Copyright (c) 2011+, HL7, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.ExpansionProfile;
import org.hl7.fhir.dstu3.model.Factory;
import org.hl7.fhir.dstu3.model.PrimitiveType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.dstu3.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.Utilities;
public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSetExpanderFactory factory;
private ValueSet focus;
private int maxExpansionSize = 500;
private int total;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super();
this.context = context;
this.factory = factory;
}
public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize;
}
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations,
ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
return null;
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system);
n.setCode(code);
if (isAbstract)
n.setAbstract(true);
if (inactive)
n.setInactive(true);
if (profile.getIncludeDesignations() && designations != null) {
for (ConceptDefinitionDesignationComponent t : designations) {
ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
}
}
ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null;
if (t == null)
n.setDisplay(display);
else
n.setDisplay(t.getValue());
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
}
return n;
}
private boolean filterContainsCode(List<ValueSet> filters, String system, String code) {
for (ValueSet vse : filters)
if (expansionContainsCode(vse.getExpansion().getContains(), system, code))
return true;
return false;
}
private boolean expansionContainsCode(List<ValueSetExpansionContainsComponent> contains, String system, String code) {
for (ValueSetExpansionContainsComponent cc : contains) {
if (system.equals(cc.getSystem()) && code.equals(cc.getCode()))
return true;
if (expansionContainsCode(cc.getContains(), system, code))
return true;
}
return false;
}
private ConceptDefinitionDesignationComponent getMatchingLang(List<ConceptDefinitionDesignationComponent> list, String lang) {
for (ConceptDefinitionDesignationComponent t : list)
if (t.getLanguage().equals(lang))
return t;
for (ConceptDefinitionDesignationComponent t : list)
if (t.getLanguage().startsWith(lang))
return t;
return null;
}
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
throws FHIRException {
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, np, profile, filters);
} else
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, null, profile, filters);
}
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly {
if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
copyImportContains(expand.getContains(), null, profile, filters);
}
private void excludeCode(String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem);
n.setCode(theCode);
String s = key(n);
excludeKeys.add(s);
}
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem());
}
if (exc.hasValueSet())
throw new Error("Processing Value set references in exclude is not yet done");
// importValueSet(imp.getValue(), params, profile);
CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
excludeCodes(context.expandVS(exc, false), params);
return;
}
for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode());
}
if (exc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(c.getSystem(), c.getCode());
}
}
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) {
if (profile == null)
profile = makeDefaultExpansion();
try {
focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID());
if (!profile.getUrl().startsWith("urn:uuid:"))
focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl()));
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
if (canBeHeirarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
focus.getExpansion().getContains().add(c);
}
} else {
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
}
}
}
if (total > 0) {
focus.getExpansion().setTotal(total);
}
return new ValueSetExpansionOutcome(focus);
} catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there?
throw e;
} catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
}
}
private ExpansionProfile makeDefaultExpansion() {
ExpansionProfile res = new ExpansionProfile();
res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase());
res.setExcludeNested(true);
res.setIncludeDesignations(false);
return res;
}
private void addToHeirarchy(List<ValueSetExpansionContainsComponent> target, List<ValueSetExpansionContainsComponent> source) {
for (ValueSetExpansionContainsComponent s : source) {
target.add(s);
}
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
return def.getDisplay();
}
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, FileNotFoundException, IOException, FHIRException {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, params, profile);
}
}
private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
if (value == null)
throw new TerminologyServiceException("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value);
if (vs == null)
throw new TerminologyServiceException("Unable to find imported value set " + value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
if (vso.getError() != null)
throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError());
if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set " + value);
if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
return vso.getValueset();
}
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) {
for (ValueSetExpansionContainsComponent c : list) {
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter);
copyImportContains(c.getContains(), np, profile, filter);
}
}
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
List<ValueSet> imports = new ArrayList<ValueSet>();
for (UriType imp : inc.getValueSet())
imports.add(importValueSet(imp.getValue(), params, profile));
if (!inc.hasSystem()) {
if (imports.isEmpty()) // though this is not supposed to be the case
return;
ValueSet base = imports.get(0);
imports.remove(0);
copyImportContains(base.getExpansion().getContains(), null, profile, imports);
} else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
addCodes(context.expandVS(inc, canBeHeirarchy), params, profile, imports);
return;
}
if (cs == null) {
if (context.isNoTerminologyServer())
throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString());
else
throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString());
}
if (cs.getContent() != CodeSystemContentMode.COMPLETE)
throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete");
if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion())));
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
}
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
}
if (inc.getFilter().size() == 1) {
ConceptSetFilterComponent fc = inc.getFilter().get(0);
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
canBeHeirarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports);
}
}
}
} else
throw new NotImplementedException("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
}
}
private List<ConceptDefinitionDesignationComponent> convertDesignations(List<ConceptReferenceDesignationComponent> list) {
List<ConceptDefinitionDesignationComponent> res = new ArrayList<CodeSystem.ConceptDefinitionDesignationComponent>();
for (ConceptReferenceDesignationComponent t : list) {
ConceptDefinitionDesignationComponent c = new ConceptDefinitionDesignationComponent();
c.setLanguage(t.getLanguage());
c.setUse(t.getUse());
c.setValue(t.getValue());
}
return res;
}
private String key(String uri, String code) {
return "{" + uri + "}" + code;
}
private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode());
}
}

View File

@ -13,6 +13,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.junit.*;
import ca.uhn.fhir.rest.annotation.IdParam;
@ -177,7 +178,7 @@ public class ContextScanningDstu3Test {
}
@Read
public Observation read(@IdParam IdType theId) {
public Observation read(@IdParam IdType theId) throws FHIRFormatError {
Observation retVal = new Observation();
retVal.setId(theId);
retVal.addIdentifier().setSystem("ISYS").setValue("IVAL");

View File

@ -1023,7 +1023,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder(ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM, ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, stringContainsInOrder(ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3, ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -1057,7 +1057,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -1077,7 +1077,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"",
assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"",
"\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));

View File

@ -1519,7 +1519,7 @@ public class XmlParserDstu3Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
@ -1666,7 +1666,7 @@ public class XmlParserDstu3Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -1688,7 +1688,7 @@ public class XmlParserDstu3Test {
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"foo\"/>", "<code value=\"bar\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));

View File

@ -504,7 +504,7 @@ public class GenericClientDstu3Test {
client.read().resource(Patient.class).withId("1").execute();
fail();
} catch (FhirClientConnectionException e) {
assertEquals("java.lang.IllegalStateException", e.getMessage());
assertEquals(null, e.getMessage());
}
try {

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.util.TestUtil;
import net.sf.json.JSON;
@ -22,16 +21,13 @@ import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
import org.hl7.fhir.instance.model.Patient.ContactComponent;
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.*;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
@ -148,7 +144,7 @@ public class JsonParserHl7OrgDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, stringContainsInOrder(Constants.TAG_SUBSETTED_SYSTEM_DSTU3, Constants.TAG_SUBSETTED_CODE));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -780,7 +776,7 @@ public class JsonParserHl7OrgDstu2Test {
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"",
"\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
"\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));
@ -802,7 +798,7 @@ public class JsonParserHl7OrgDstu2Test {
assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"",
"\"system\": \"foo\",", "\"code\": \"bar\"",
"\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
"\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus")));

View File

@ -19,8 +19,6 @@ import org.hl7.fhir.instance.model.Enumeration;
import org.hl7.fhir.instance.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.instance.model.Identifier.IdentifierUse;
import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
import org.hl7.fhir.instance.model.api.*;
import org.junit.*;
import org.hl7.fhir.instance.model.Observation;
import org.hl7.fhir.instance.model.Organization;
import org.hl7.fhir.instance.model.Patient;
@ -32,7 +30,6 @@ import org.hl7.fhir.instance.model.Specimen;
import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.After;
import org.junit.Assert;
@ -49,7 +46,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredAddressExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredExtension;
import ca.uhn.fhir.rest.api.Constants;
@ -1050,7 +1046,7 @@ public class XmlParserHl7OrgDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV")));
@ -1169,7 +1165,7 @@ public class XmlParserHl7OrgDstu2Test {
ourLog.info(encoded);
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));
@ -1191,7 +1187,7 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"foo\"/>", "<code value=\"bar\"/>", "</tag>"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM + "\"/>",
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"" + Constants.TAG_SUBSETTED_SYSTEM_DSTU3 + "\"/>",
"<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family"));

View File

@ -1,12 +1,9 @@
package org.hl7.fhir.r4.conformance;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.IWorkerContext;
@ -29,7 +26,7 @@ public class ConstraintJavaGenerator {
}
public String generate(StructureDefinition sd) throws FHIRException, IOException {
String name = Utilities.titleize(sd.getName().replace(".", "").replace("-", "").replace("\"", "")).replace(" ", "");
String name = sd.hasName() ? Utilities.titleize(sd.getName().replace(".", "").replace("-", "").replace("\"", "")).replace(" ", "") : "";
if (!Utilities.nmtokenize(name).equals(name)) {
System.out.println("Cannot generate Java code for profile "+sd.getUrl()+" because the name \""+name+"\" is not a valid Java class name");
return null;
@ -46,6 +43,7 @@ public class ConstraintJavaGenerator {
dest.write("}\r\n");
dest.flush();
dest.close();
return destFile.getAbsolutePath();
}

View File

@ -1,6 +1,9 @@
package org.hl7.fhir.r4.conformance;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -8,6 +11,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r4.conformance.ProfileComparer.ProfileComparison;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.formats.IParser;
import org.hl7.fhir.r4.model.Base;
@ -16,6 +22,7 @@ import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingComponent;
import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
@ -34,13 +41,15 @@ import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.DefinitionNavigator;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Logger.LogMessageType;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import com.google.gson.JsonObject;
/**
* A engine that generates difference analysis between two sets of structure
* definitions, typically from 2 different implementation guides.
@ -398,8 +407,11 @@ public class ProfileComparer {
if (isExtension(left.path()))
return compareExtensions(outcome, path, superset, subset, left, right);
// return true;
else
else {
ElementDefinitionSlicingComponent slicingL = left.current().getSlicing();
ElementDefinitionSlicingComponent slicingR = right.current().getSlicing();
throw new DefinitionException("Slicing is not handled yet");
}
// todo: name
}
@ -629,8 +641,8 @@ public class ProfileComparer {
return false;
}
}
subBinding.setValueSet(new Reference().setReference("#"+addValueSet(cvs)));
superBinding.setValueSet(new Reference().setReference("#"+addValueSet(unite(superset, outcome, path, lvs, rvs))));
subBinding.setValueSet("#"+addValueSet(cvs));
superBinding.setValueSet("#"+addValueSet(unite(superset, outcome, path, lvs, rvs)));
}
}
return false;
@ -649,11 +661,11 @@ public class ProfileComparer {
ValueSet lvs = resolveVS(outcome.left, left.getValueSet());
ValueSet rvs = resolveVS(outcome.left, right.getValueSet());
if (lvs != null && rvs != null)
union.setValueSet(new Reference().setReference("#"+addValueSet(unite(ed, outcome, path, lvs, rvs))));
union.setValueSet("#"+addValueSet(unite(ed, outcome, path, lvs, rvs)));
else if (lvs != null)
union.setValueSet(new Reference().setReference("#"+addValueSet(lvs)));
union.setValueSet("#"+addValueSet(lvs));
else if (rvs != null)
union.setValueSet(new Reference().setReference("#"+addValueSet(rvs)));
union.setValueSet("#"+addValueSet(rvs));
}
return union;
}
@ -710,17 +722,10 @@ public class ProfileComparer {
return false;
}
private ValueSet resolveVS(StructureDefinition ctxtLeft, Type vsRef) {
private ValueSet resolveVS(StructureDefinition ctxtLeft, String vsRef) {
if (vsRef == null)
return null;
if (vsRef instanceof UriType)
return null;
else {
Reference ref = (Reference) vsRef;
if (!ref.hasReference())
return null;
return context.fetchResource(ValueSet.class, ref.getReference());
}
return context.fetchResource(ValueSet.class, vsRef);
}
private ValueSet intersectByDefinition(ValueSet lvs, ValueSet rvs) {
@ -1166,6 +1171,100 @@ public class ProfileComparer {
this.rightName = rightName;
}
private String genPCLink(String leftName, String leftLink) {
return "<a href=\""+leftLink+"\">"+Utilities.escapeXml(leftName)+"</a>";
}
private String genPCTable() {
StringBuilder b = new StringBuilder();
b.append("<table class=\"grid\">\r\n");
b.append("<tr>");
b.append(" <td><b>Left</b></td>");
b.append(" <td><b>Right</b></td>");
b.append(" <td><b>Comparison</b></td>");
b.append(" <td><b>Error #</b></td>");
b.append(" <td><b>Warning #</b></td>");
b.append(" <td><b>Hint #</b></td>");
b.append("</tr>");
for (ProfileComparison cmp : getComparisons()) {
b.append("<tr>");
b.append(" <td><a href=\""+cmp.getLeft().getUserString("path")+"\">"+Utilities.escapeXml(cmp.getLeft().getName())+"</a></td>");
b.append(" <td><a href=\""+cmp.getRight().getUserString("path")+"\">"+Utilities.escapeXml(cmp.getRight().getName())+"</a></td>");
b.append(" <td><a href=\""+getId()+"."+cmp.getId()+".html\">Click Here</a></td>");
b.append(" <td>"+cmp.getErrorCount()+"</td>");
b.append(" <td>"+cmp.getWarningCount()+"</td>");
b.append(" <td>"+cmp.getHintCount()+"</td>");
b.append("</tr>");
}
b.append("</table>\r\n");
return b.toString();
}
public String generate(String dest) throws IOException {
// ok, all compared; now produce the output
// first page we produce is simply the index
Map<String, String> vars = new HashMap<String, String>();
vars.put("title", getTitle());
vars.put("left", genPCLink(getLeftName(), getLeftLink()));
vars.put("right", genPCLink(getRightName(), getRightLink()));
vars.put("table", genPCTable());
producePage(summaryTemplate(), Utilities.path(dest, getId()+".html"), vars);
// page.log(" ... generate", LogMessageType.Process);
// String src = TextFile.fileToString(page.getFolders().srcDir + "template-comparison-set.html");
// src = page.processPageIncludes(n+".html", src, "?type", null, "??path", null, null, "Comparison", pc, null, null, page.getDefinitions().getWorkgroups().get("fhir"));
// TextFile.stringToFile(src, Utilities.path(page.getFolders().dstDir, n+".html"));
// cachePage(n + ".html", src, "Comparison "+pc.getTitle(), false);
//
// // then we produce a comparison page for each pair
// for (ProfileComparison cmp : pc.getComparisons()) {
// src = TextFile.fileToString(page.getFolders().srcDir + "template-comparison.html");
// src = page.processPageIncludes(n+"."+cmp.getId()+".html", src, "?type", null, "??path", null, null, "Comparison", cmp, null, null, page.getDefinitions().getWorkgroups().get("fhir"));
// TextFile.stringToFile(src, Utilities.path(page.getFolders().dstDir, n+"."+cmp.getId()+".html"));
// cachePage(n +"."+cmp.getId()+".html", src, "Comparison "+pc.getTitle(), false);
// }
// // and also individual pages for each pair outcome
// // then we produce value set pages for each value set
//
// // TODO Auto-generated method stub
return null;
}
private void producePage(String src, String path, Map<String, String> vars) throws IOException {
while (src.contains("[%"))
{
int i1 = src.indexOf("[%");
int i2 = src.substring(i1).indexOf("%]")+i1;
String s1 = src.substring(0, i1);
String s2 = src.substring(i1 + 2, i2).trim();
String s3 = src.substring(i2+2);
String v = vars.containsKey(s2) ? vars.get(s2) : "???";
src = s1+v+s3;
}
TextFile.stringToFile(src, path);
}
private String summaryTemplate() throws IOException {
return cachedFetch("04a9d69a-47f2-4250-8645-bf5d880a8eaa-1.fhir-template", "http://build.fhir.org/template-comparison-set.html.template");
}
private String cachedFetch(String id, String source) throws IOException {
String tmpDir = System.getProperty("java.io.tmpdir");
String local = Utilities.path(tmpDir, id);
File f = new File(local);
if (f.exists())
return TextFile.fileToString(f);
URL url = new URL(source);
URLConnection c = url.openConnection();
String result = TextFile.streamToString(c.getInputStream());
TextFile.stringToFile(result, f);
return result;
}

View File

@ -11,6 +11,7 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.elementmodel.TurtleParser;
import org.hl7.fhir.r4.model.DomainResource;
@ -24,7 +25,6 @@ import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.FHIRException;
import org.stringtemplate.v4.ST;
public class ShExGenerator {
@ -388,7 +388,7 @@ public class ShExGenerator {
for (String dt : new HashSet<String>(datatypes)) {
if (!emittedDatatypes.contains(dt)) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class,
ProfileUtilities.sdNs(dt));
ProfileUtilities.sdNs(dt, null));
// TODO: Figure out why the line below doesn't work
// if (sd != null && !uniq_structures.contains(sd))
if(sd != null && !uniq_structure_urls.contains(sd.getUrl()))
@ -748,23 +748,11 @@ public class ShExGenerator {
// TODO: find a utility that implements this
private ValueSet resolveBindingReference(DomainResource ctxt, Type reference) {
if (reference instanceof UriType) {
return context.fetchResource(ValueSet.class, ((UriType) reference).getValue().toString());
}
else if (reference instanceof Reference) {
String s = ((Reference) reference).getReference();
if (s.startsWith("#")) {
for (Resource c : ctxt.getContained()) {
if (c.getId().equals(s.substring(1)) && (c instanceof ValueSet))
return (ValueSet) c;
}
return null;
} else {
return context.fetchResource(ValueSet.class, ((Reference) reference).getReference());
}
}
else
private ValueSet resolveBindingReference(DomainResource ctxt, String reference) {
try {
return context.fetchResource(ValueSet.class, reference);
} catch (Throwable e) {
return null;
}
}
}

View File

@ -0,0 +1,520 @@
package org.hl7.fhir.r4.conformance;
import java.io.FileOutputStream;
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.PropertyRepresentation;
import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
public class XmlSchemaGenerator {
public class QName {
public String type;
public String typeNs;
@Override
public String toString() {
return typeNs+":"+type;
}
}
public class ElementToGenerate {
private String tname;
private StructureDefinition sd;
private ElementDefinition ed;
public ElementToGenerate(String tname, StructureDefinition sd, ElementDefinition edc) {
this.tname = tname;
this.sd = sd;
this.ed = edc;
}
}
private String folder;
private IWorkerContext context;
private boolean single;
private String version;
private String genDate;
private String license;
private boolean annotations;
public XmlSchemaGenerator(String folder, IWorkerContext context) {
this.folder = folder;
this.context = context;
}
public boolean isSingle() {
return single;
}
public void setSingle(boolean single) {
this.single = single;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getGenDate() {
return genDate;
}
public void setGenDate(String genDate) {
this.genDate = genDate;
}
public String getLicense() {
return license;
}
public void setLicense(String license) {
this.license = license;
}
public boolean isAnnotations() {
return annotations;
}
public void setAnnotations(boolean annotations) {
this.annotations = annotations;
}
private Set<ElementDefinition> processed = new HashSet<ElementDefinition>();
private Set<StructureDefinition> processedLibs = new HashSet<StructureDefinition>();
private Set<String> typeNames = new HashSet<String>();
private OutputStreamWriter writer;
private Map<String, String> namespaces = new HashMap<String, String>();
private Queue<ElementToGenerate> queue = new LinkedList<ElementToGenerate>();
private Queue<StructureDefinition> queueLib = new LinkedList<StructureDefinition>();
private Map<String, StructureDefinition> library;
private boolean useNarrative;
private void w(String s) throws IOException {
writer.write(s);
}
private void ln(String s) throws IOException {
writer.write(s);
writer.write("\r\n");
}
private void close() throws IOException {
if (writer != null) {
ln("</xs:schema>");
writer.flush();
writer.close();
writer = null;
}
}
private String start(StructureDefinition sd, String ns) throws IOException, FHIRException {
String lang = "en";
if (sd.hasLanguage())
lang = sd.getLanguage();
if (single && writer != null) {
if (!ns.equals(getNs(sd)))
throw new FHIRException("namespace inconsistency: "+ns+" vs "+getNs(sd));
return lang;
}
close();
writer = new OutputStreamWriter(new FileOutputStream(Utilities.path(folder, tail(sd.getType()+".xsd"))), "UTF-8");
ln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
ln("<!-- ");
ln(license);
ln("");
ln(" Generated on "+genDate+" for FHIR v"+version+" ");
ln("");
ln(" Note: this schema does not contain all the knowledge represented in the underlying content model");
ln("");
ln("-->");
ln("<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:fhir=\"http://hl7.org/fhir\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" "+
"xmlns:lm=\""+ns+"\" targetNamespace=\""+ns+"\" elementFormDefault=\"qualified\" version=\"1.0\">");
ln(" <xs:import schemaLocation=\"fhir-common.xsd\" namespace=\"http://hl7.org/fhir\"/>");
if (useNarrative) {
if (ns.equals("urn:hl7-org:v3"))
ln(" <xs:include schemaLocation=\"cda-narrative.xsd\"/>");
else
ln(" <xs:import schemaLocation=\"cda-narrative.xsd\" namespace=\"urn:hl7-org:v3\"/>");
}
namespaces.clear();
namespaces.put(ns, "lm");
namespaces.put("http://hl7.org/fhir", "fhir");
typeNames.clear();
return lang;
}
private String getNs(StructureDefinition sd) {
String ns = "http://hl7.org/fhir";
if (sd.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
ns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
return ns;
}
public void generate(StructureDefinition entry, Map<String, StructureDefinition> library) throws Exception {
processedLibs.clear();
this.library = library;
checkLib(entry);
String ns = getNs(entry);
String lang = start(entry, ns);
w(" <xs:element name=\""+tail(entry.getType())+"\" type=\"lm:"+tail(entry.getType())+"\"");
if (annotations) {
ln(">");
ln(" <xs:annotation>");
ln(" <xs:documentation xml:lang=\""+lang+"\">"+Utilities.escapeXml(entry.getDescription())+"</xs:documentation>");
ln(" </xs:annotation>");
ln(" </xs:element>");
} else
ln("/>");
produceType(entry, entry.getSnapshot().getElement().get(0), tail(entry.getType()), getQN(entry, entry.getBaseDefinition()), lang);
while (!queue.isEmpty()) {
ElementToGenerate q = queue.poll();
produceType(q.sd, q.ed, q.tname, getQN(q.sd, q.ed, "http://hl7.org/fhir/StructureDefinition/Element", false), lang);
}
while (!queueLib.isEmpty()) {
generateInner(queueLib.poll());
}
close();
}
private void checkLib(StructureDefinition entry) {
for (ElementDefinition ed : entry.getSnapshot().getElement()) {
if (ed.hasRepresentation(PropertyRepresentation.CDATEXT)) {
useNarrative = true;
}
}
for (StructureDefinition sd : library.values()) {
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.hasRepresentation(PropertyRepresentation.CDATEXT)) {
useNarrative = true;
}
}
}
}
private void generateInner(StructureDefinition sd) throws IOException, FHIRException {
if (processedLibs.contains(sd))
return;
processedLibs.add(sd);
String ns = getNs(sd);
String lang = start(sd, ns);
if (sd.getSnapshot().getElement().isEmpty())
throw new FHIRException("no snap shot on "+sd.getUrl());
produceType(sd, sd.getSnapshot().getElement().get(0), tail(sd.getType()), getQN(sd, sd.getBaseDefinition()), lang);
while (!queue.isEmpty()) {
ElementToGenerate q = queue.poll();
produceType(q.sd, q.ed, q.tname, getQN(q.sd, q.ed, "http://hl7.org/fhir/StructureDefinition/Element", false), lang);
}
}
private String tail(String url) {
return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url;
}
private String root(String url) {
return url.contains("/") ? url.substring(0, url.lastIndexOf("/")) : "";
}
private String tailDot(String url) {
return url.contains(".") ? url.substring(url.lastIndexOf(".")+1) : url;
}
private void produceType(StructureDefinition sd, ElementDefinition ed, String typeName, QName typeParent, String lang) throws IOException, FHIRException {
if (processed.contains(ed))
return;
processed.add(ed);
// ok
ln(" <xs:complexType name=\""+typeName+"\">");
if (annotations) {
ln(" <xs:annotation>");
ln(" <xs:documentation xml:lang=\""+lang+"\">"+Utilities.escapeXml(ed.getDefinition())+"</xs:documentation>");
ln(" </xs:annotation>");
}
ln(" <xs:complexContent>");
ln(" <xs:extension base=\""+typeParent.toString()+"\">");
ln(" <xs:sequence>");
// hack....
for (ElementDefinition edc : ProfileUtilities.getChildList(sd, ed)) {
if (!(edc.hasRepresentation(PropertyRepresentation.XMLATTR) || edc.hasRepresentation(PropertyRepresentation.XMLTEXT)) && !inheritedElement(edc))
produceElement(sd, ed, edc, lang);
}
ln(" </xs:sequence>");
for (ElementDefinition edc : ProfileUtilities.getChildList(sd, ed)) {
if ((edc.hasRepresentation(PropertyRepresentation.XMLATTR) || edc.hasRepresentation(PropertyRepresentation.XMLTEXT)) && !inheritedElement(edc))
produceAttribute(sd, ed, edc, lang);
}
ln(" </xs:extension>");
ln(" </xs:complexContent>");
ln(" </xs:complexType>");
}
private boolean inheritedElement(ElementDefinition edc) {
return !edc.getPath().equals(edc.getBase().getPath());
}
private void produceElement(StructureDefinition sd, ElementDefinition ed, ElementDefinition edc, String lang) throws IOException, FHIRException {
if (edc.getType().size() == 0)
throw new Error("No type at "+edc.getPath());
if (edc.getType().size() > 1 && edc.hasRepresentation(PropertyRepresentation.TYPEATTR)) {
// first, find the common base type
StructureDefinition lib = getCommonAncestor(edc.getType());
if (lib == null)
throw new Error("Common ancester not found at "+edc.getPath());
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (TypeRefComponent t : edc.getType()) {
b.append(getQN(sd, edc, t.getCode(), true).toString());
}
String name = tailDot(edc.getPath());
String min = String.valueOf(edc.getMin());
String max = edc.getMax();
if ("*".equals(max))
max = "unbounded";
QName qn = getQN(sd, edc, lib.getUrl(), true);
ln(" <xs:element name=\""+name+"\" minOccurs=\""+min+"\" maxOccurs=\""+max+"\" type=\""+qn.typeNs+":"+qn.type+"\">");
ln(" <xs:annotation>");
ln(" <xs:appinfo xml:lang=\"en\">Possible types: "+b.toString()+"</xs:appinfo>");
if (annotations && edc.hasDefinition())
ln(" <xs:documentation xml:lang=\""+lang+"\">"+Utilities.escapeXml(edc.getDefinition())+"</xs:documentation>");
ln(" </xs:annotation>");
ln(" </xs:element>");
} else for (TypeRefComponent t : edc.getType()) {
String name = tailDot(edc.getPath());
if (edc.getType().size() > 1)
name = name + Utilities.capitalize(t.getCode());
QName qn = getQN(sd, edc, t.getCode(), true);
String min = String.valueOf(edc.getMin());
String max = edc.getMax();
if ("*".equals(max))
max = "unbounded";
w(" <xs:element name=\""+name+"\" minOccurs=\""+min+"\" maxOccurs=\""+max+"\" type=\""+qn.typeNs+":"+qn.type+"\"");
if (annotations && edc.hasDefinition()) {
ln(">");
ln(" <xs:annotation>");
ln(" <xs:documentation xml:lang=\""+lang+"\">"+Utilities.escapeXml(edc.getDefinition())+"</xs:documentation>");
ln(" </xs:annotation>");
ln(" </xs:element>");
} else
ln("/>");
}
}
public QName getQN(StructureDefinition sd, String type) throws FHIRException {
return getQN(sd, sd.getSnapshot().getElementFirstRep(), type, false);
}
public QName getQN(StructureDefinition sd, ElementDefinition edc, String t, boolean chase) throws FHIRException {
QName qn = new QName();
qn.type = Utilities.isAbsoluteUrl(t) ? tail(t) : t;
if (Utilities.isAbsoluteUrl(t)) {
String ns = root(t);
if (ns.equals(root(sd.getUrl())))
ns = getNs(sd);
if (ns.equals("http://hl7.org/fhir/StructureDefinition"))
ns = "http://hl7.org/fhir";
if (!namespaces.containsKey(ns))
throw new FHIRException("Unknown type namespace "+ns+" for "+edc.getPath());
qn.typeNs = namespaces.get(ns);
StructureDefinition lib = library.get(t);
if (lib == null && !Utilities.existsInList(t, "http://hl7.org/fhir/cda/StructureDefinition/StrucDoc.Text", "http://hl7.org/fhir/StructureDefinition/Element"))
throw new FHIRException("Unable to resolve "+t+" for "+edc.getPath());
if (lib != null)
queueLib.add(lib);
} else
qn.typeNs = namespaces.get("http://hl7.org/fhir");
if (chase && qn.type.equals("Element")) {
String tname = typeNameFromPath(edc);
if (typeNames.contains(tname)) {
int i = 1;
while (typeNames.contains(tname+i))
i++;
tname = tname+i;
}
queue.add(new ElementToGenerate(tname, sd, edc));
qn.typeNs = "lm";
qn.type = tname;
}
return qn;
}
private StructureDefinition getCommonAncestor(List<TypeRefComponent> type) throws FHIRException {
StructureDefinition sd = library.get(type.get(0).getCode());
if (sd == null)
throw new FHIRException("Unable to find definition for "+type.get(0).getCode());
for (int i = 1; i < type.size(); i++) {
StructureDefinition t = library.get(type.get(i).getCode());
if (t == null)
throw new FHIRException("Unable to find definition for "+type.get(i).getCode());
sd = getCommonAncestor(sd, t);
}
return sd;
}
private StructureDefinition getCommonAncestor(StructureDefinition sd1, StructureDefinition sd2) throws FHIRException {
// this will always return something because everything comes from Element
List<StructureDefinition> chain1 = new ArrayList<>();
List<StructureDefinition> chain2 = new ArrayList<>();
chain1.add(sd1);
chain2.add(sd2);
StructureDefinition root = library.get("Element");
StructureDefinition common = findIntersection(chain1, chain2);
boolean chain1Done = false;
boolean chain2Done = false;
while (common == null) {
chain1Done = checkChain(chain1, root, chain1Done);
chain2Done = checkChain(chain2, root, chain2Done);
if (chain1Done && chain2Done)
return null;
common = findIntersection(chain1, chain2);
}
return common;
}
private StructureDefinition findIntersection(List<StructureDefinition> chain1, List<StructureDefinition> chain2) {
for (StructureDefinition sd1 : chain1)
for (StructureDefinition sd2 : chain2)
if (sd1 == sd2)
return sd1;
return null;
}
public boolean checkChain(List<StructureDefinition> chain1, StructureDefinition root, boolean chain1Done) throws FHIRException {
if (!chain1Done) {
StructureDefinition sd = chain1.get(chain1.size()-1);
String bu = sd.getBaseDefinition();
if (bu == null)
throw new FHIRException("No base definition for "+sd.getUrl());
StructureDefinition t = library.get(bu);
if (t == null)
chain1Done = true;
else
chain1.add(t);
}
return chain1Done;
}
private StructureDefinition getBase(StructureDefinition structureDefinition) {
return null;
}
private String typeNameFromPath(ElementDefinition edc) {
StringBuilder b = new StringBuilder();
boolean up = true;
for (char ch : edc.getPath().toCharArray()) {
if (ch == '.')
up = true;
else if (up) {
b.append(Character.toUpperCase(ch));
up = false;
} else
b.append(ch);
}
return b.toString();
}
private void produceAttribute(StructureDefinition sd, ElementDefinition ed, ElementDefinition edc, String lang) throws IOException, FHIRException {
TypeRefComponent t = edc.getTypeFirstRep();
String name = tailDot(edc.getPath());
String min = String.valueOf(edc.getMin());
String max = edc.getMax();
// todo: check it's a code...
// if (!max.equals("1"))
// throw new FHIRException("Illegal cardinality \""+max+"\" for attribute "+edc.getPath());
if (Utilities.isAbsoluteUrl(t.getCode()))
throw new FHIRException("Only FHIR primitive types are supported for attributes ("+t.getCode()+")");
String typeNs = namespaces.get("http://hl7.org/fhir");
String type = t.getCode();
w(" <xs:attribute name=\""+name+"\" use=\""+(min.equals("0") || edc.hasFixed() || edc.hasDefaultValue() ? "optional" : "required")+"\" type=\""+typeNs+":"+type+(typeNs.equals("fhir") ? "-primitive" : "")+"\""+
(edc.hasFixed() ? " fixed=\""+edc.getFixed().primitiveValue()+"\"" : "")+(edc.hasDefaultValue() && !edc.hasFixed() ? " default=\""+edc.getDefaultValue().primitiveValue()+"\"" : "")+"");
if (annotations && edc.hasDefinition()) {
ln(">");
ln(" <xs:annotation>");
ln(" <xs:documentation xml:lang=\""+lang+"\">"+Utilities.escapeXml(edc.getDefinition())+"</xs:documentation>");
ln(" </xs:annotation>");
ln(" </xs:attribute>");
} else
ln("/>");
}
}

View File

@ -11,8 +11,8 @@ import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r4.model.ExpansionProfile;
import org.hl7.fhir.r4.model.MetadataResource;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureMap;
@ -194,8 +194,8 @@ public interface IWorkerContext {
// -- Terminology services ------------------------------------------------------
public ExpansionProfile getExpansionProfile();
public void setExpansionProfile(ExpansionProfile expProfile);
public Parameters getExpansionParameters();
public void setExpansionProfile(Parameters expParameters);
// these are the terminology services used internally by the tools
/**
@ -256,7 +256,7 @@ public interface IWorkerContext {
* @return
* @throws FHIRException
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException;
public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException;
public class ValidationResult {
private ConceptDefinitionComponent definition;
@ -286,7 +286,7 @@ public interface IWorkerContext {
}
public boolean isOk() {
return definition != null;
return severity == null || severity == IssueSeverity.INFORMATION || severity == IssueSeverity.WARNING;
}
public String getDisplay() {
@ -314,6 +314,16 @@ public interface IWorkerContext {
public TerminologyServiceErrorClass getErrorClass() {
return errorClass;
}
public ValidationResult setSeverity(IssueSeverity severity) {
this.severity = severity;
return this;
}
public ValidationResult setMessage(String message) {
this.message = message;
return this;
}
}
@ -348,6 +358,7 @@ public interface IWorkerContext {
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(String code, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
@ -391,10 +402,16 @@ public interface IWorkerContext {
}
public void setLogger(ILoggingService logger);
public ILoggingService getLogger();
public boolean isNoTerminologyServer();
public TranslationServices translator();
public List<StructureMap> listTransforms();
public StructureMap getTransform(String url);
public String getOverrideVersionNs();
public void setOverrideVersionNs(String value);
public StructureDefinition fetchTypeDefinition(String typeName);
}

View File

@ -61,6 +61,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.CSFileInputStream;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.cache.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
@ -92,12 +93,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
private String date;
private IValidatorFactory validatorFactory;
private UcumService ucumService;
private boolean ignoreProfileErrors;
public SimpleWorkerContext() {
public SimpleWorkerContext() throws FileNotFoundException, IOException, FHIRException {
super();
}
public SimpleWorkerContext(SimpleWorkerContext other) {
public SimpleWorkerContext(SimpleWorkerContext other) throws FileNotFoundException, IOException, FHIRException {
super();
copy(other);
}
@ -130,6 +132,26 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return res;
}
public static SimpleWorkerContext fromPackage(NpmPackage pi, boolean allowDuplicates) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext();
res.setAllowLoadingDuplicates(allowDuplicates);
res.loadFromPackage(pi, null);
return res;
}
public static SimpleWorkerContext fromPackage(NpmPackage pi) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext();
res.loadFromPackage(pi, null);
return res;
}
public static SimpleWorkerContext fromPackage(NpmPackage pi, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext();
res.setAllowLoadingDuplicates(true);
res.loadFromPackage(pi, loader);
return res;
}
public static SimpleWorkerContext fromPack(String path, boolean allowDuplicates) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext();
res.setAllowLoadingDuplicates(allowDuplicates);
@ -224,29 +246,43 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
}
private void loadFromFileJson(InputStream stream, String name, IContextResourceLoader loader) throws IOException, FHIRException {
Bundle f;
Bundle f = null;
try {
if (loader != null)
f = loader.loadBundle(stream, true);
else {
JsonParser json = new JsonParser();
f = (Bundle) json.parse(stream);
Resource r = json.parse(stream);
if (r instanceof Bundle)
f = (Bundle) r;
else
cacheResource(r);
}
} catch (FHIRFormatError e1) {
throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1);
}
for (BundleEntryComponent e : f.getEntry()) {
if (e.getFullUrl() == null) {
logger.logDebugMessage(LogCategory.CONTEXT, "unidentified resource in " + name+" (no fullUrl)");
}
cacheResource(e.getResource());
if (f != null)
for (BundleEntryComponent e : f.getEntry()) {
cacheResource(e.getResource());
}
}
private void loadFromPack(String path, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException {
loadFromStream(new CSFileInputStream(path), loader);
}
public void loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String... types) throws FileNotFoundException, IOException, FHIRException {
if (types.length == 0)
types = new String[] { "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"};
for (String s : pi.list("package")) {
if (s.contains("-") && s.endsWith(".json")) {
String t = s.substring(0, s.indexOf("-"));
if (Utilities.existsInList(t, types)) {
loadDefinitionItem(s, pi.load("package", s), loader);
}
}
}
}
public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException {
loadDefinitionItem(file, new CSFileInputStream(file), loader);
@ -423,6 +459,18 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return result;
}
public void loadBinariesFromFolder(String folder) throws FileNotFoundException, Exception {
for (String n : new File(folder).list()) {
loadBytes(n, new FileInputStream(Utilities.path(folder, n)));
}
}
public void loadBinariesFromFolder(NpmPackage pi) throws FileNotFoundException, Exception {
for (String n : pi.list("other")) {
loadBytes(n, pi.load("other", n));
}
}
public void loadFromFolder(String folder) throws FileNotFoundException, Exception {
for (String n : new File(folder).list()) {
if (n.endsWith(".json"))
@ -507,12 +555,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
List<String> errors = new ArrayList<String>();
ProfileUtilities pu = new ProfileUtilities(this, msgs, this);
pu.setThrowException(false);
pu.sortDifferential(sd, p, p.getUrl(), errors);
for (String err : errors)
msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getUserString("path"), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR));
pu.generateSnapshot(sd, p, p.getUrl(), p.getName());
for (ValidationMessage msg : msgs) {
if (msg.getLevel() == ValidationMessage.IssueSeverity.ERROR || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+"). Error generating snapshot: "+msg.getMessage());
}
if (!p.hasSnapshot())
@ -531,6 +580,14 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
this.ucumService = ucumService;
}
public boolean isIgnoreProfileErrors() {
return ignoreProfileErrors;
}
public void setIgnoreProfileErrors(boolean ignoreProfileErrors) {
this.ignoreProfileErrors = ignoreProfileErrors;
}

View File

@ -0,0 +1,426 @@
package org.hl7.fhir.r4.context;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* This implements a two level cache.
* - a temporary cache for remmbering previous local operations
* - a persistent cache for rembering tx server operations
*
* the cache is a series of pairs: a map, and a list. the map is the loaded cache, the list is the persiistent cache, carefully maintained in order for version control consistency
*
* @author graha
*
*/
public class TerminologyCache {
public static final boolean TRANSIENT = false;
public static final boolean PERMANENT = true;
private static final String NAME_FOR_NO_SYSTEM = "all-systems";
private static final String ENTRY_MARKER = "-------------------------------------------------------------------------------------";
private static final String BREAK = "####";
public class CacheToken {
private String name;
private String key;
private String request;
public void setName(String n) {
if (name == null)
name = n;
else if (!n.equals(name))
name = NAME_FOR_NO_SYSTEM;
}
}
private class CacheEntry {
private String request;
private boolean persistent;
private ValidationResult v;
private ValueSetExpansionOutcome e;
}
private class NamedCache {
private String name;
private List<CacheEntry> list = new ArrayList<CacheEntry>(); // persistent entries
private Map<String, CacheEntry> map = new HashMap<String, CacheEntry>();
}
private Object lock;
private String folder;
private Map<String, NamedCache> caches = new HashMap<String, NamedCache>();
// use lock from the context
public TerminologyCache(Object lock, String folder) throws FileNotFoundException, IOException, FHIRException {
super();
this.lock = lock;
this.folder = folder;
if (folder != null)
load();
}
public CacheToken generateValidationToken(Coding code, ValueSet vs) {
CacheToken ct = new CacheToken();
if (code.hasSystem())
ct.name = getNameForSystem(code.getSystem());
else
ct.name = NAME_FOR_NO_SYSTEM;
JsonParser json = new JsonParser();
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+"}";
} catch (IOException e) {
throw new Error(e);
}
ct.key = String.valueOf(hashNWS(ct.request));
return ct;
}
public CacheToken generateValidationToken(CodeableConcept code, ValueSet vs) {
CacheToken ct = new CacheToken();
for (Coding c : code.getCoding()) {
if (c.hasSystem())
ct.setName(getNameForSystem(c.getSystem()));
}
JsonParser json = new JsonParser();
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+"}";
} catch (IOException e) {
throw new Error(e);
}
ct.key = String.valueOf(hashNWS(ct.request));
return ct;
}
public ValueSet getVSEssense(ValueSet vs) {
if (vs == null)
return null;
ValueSet vsc = new ValueSet();
vsc.setCompose(vs.getCompose());
if (vs.hasExpansion()) {
vsc.getExpansion().getParameter().addAll(vs.getExpansion().getParameter());
vsc.getExpansion().getContains().addAll(vs.getExpansion().getContains());
}
return vsc;
}
public CacheToken generateExpandToken(ValueSet vs, boolean heirarchical) {
CacheToken ct = new CacheToken();
ValueSet vsc = getVSEssense(vs);
for (ConceptSetComponent inc : vs.getCompose().getInclude())
if (inc.hasSystem())
ct.setName(getNameForSystem(inc.getSystem()));
for (ConceptSetComponent inc : vs.getCompose().getExclude())
if (inc.hasSystem())
ct.setName(getNameForSystem(inc.getSystem()));
for (ValueSetExpansionContainsComponent inc : vs.getExpansion().getContains())
if (inc.hasSystem())
ct.setName(getNameForSystem(inc.getSystem()));
JsonParser json = new JsonParser();
json.setOutputStyle(OutputStyle.PRETTY);
try {
ct.request = "{\"hierarchical\" : "+(heirarchical ? "true" : "false")+", \"valueSet\" :"+json.composeString(vsc)+"}\r\n";
} catch (IOException e) {
throw new Error(e);
}
ct.key = String.valueOf(hashNWS(ct.request));
return ct;
}
private String getNameForSystem(String system) {
if (system.equals("http://snomed.info/sct"))
return "snomed";
if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm"))
return "rxnorm";
if (system.equals("http://loinc.org"))
return "loinc";
if (system.equals("http://unitsofmeasure.org"))
return "ucum";
if (system.startsWith("http://hl7.org/fhir/sid/"))
return system.substring(24).replace("/", "");
if (system.startsWith("urn:iso:std:iso:"))
return "iso"+system.substring(16).replace(":", "");
if (system.startsWith("http://terminology.hl7.org/CodeSystem/"))
return system.substring(38).replace("/", "");
if (system.startsWith("http://hl7.org/fhir/"))
return system.substring(20).replace("/", "");
if (system.equals("urn:ietf:bcp:47"))
return "lang";
if (system.equals("urn:ietf:bcp:13"))
return "mimetypes";
if (system.equals("urn:iso:std:iso:11073:10101"))
return "11073";
if (system.equals("http://dicom.nema.org/resources/ontology/DCM"))
return "dicom";
return system.replace("/", "_").replace(":", "_");
}
public NamedCache getNamedCache(CacheToken cacheToken) {
NamedCache nc = caches.get(cacheToken.name);
if (nc == null) {
nc = new NamedCache();
nc.name = cacheToken.name;
caches.put(nc.name, nc);
}
return nc;
}
public ValueSetExpansionOutcome getExpansion(CacheToken cacheToken) {
synchronized (lock) {
NamedCache nc = getNamedCache(cacheToken);
CacheEntry e = nc.map.get(cacheToken.key);
if (e == null)
return null;
else
return e.e;
}
}
public void cacheExpansion(CacheToken cacheToken, ValueSetExpansionOutcome res, boolean persistent) {
synchronized (lock) {
NamedCache nc = getNamedCache(cacheToken);
CacheEntry e = new CacheEntry();
e.request = cacheToken.request;
e.persistent = persistent;
e.e = res;
store(cacheToken, persistent, nc, e);
}
}
public void store(CacheToken cacheToken, boolean persistent, NamedCache nc, CacheEntry e) {
boolean n = nc.map.containsKey(cacheToken.key);
nc.map.put(cacheToken.key, e);
if (persistent) {
if (n) {
for (int i = nc.list.size()- 1; i>= 0; i--) {
if (nc.list.get(i).request.equals(e.request)) {
nc.list.remove(i);
}
}
}
nc.list.add(e);
save(nc);
}
}
public ValidationResult getValidation(CacheToken cacheToken) {
synchronized (lock) {
NamedCache nc = getNamedCache(cacheToken);
CacheEntry e = nc.map.get(cacheToken.key);
if (e == null)
return null;
else
return e.v;
}
}
public void cacheValidation(CacheToken cacheToken, ValidationResult res, boolean persistent) {
synchronized (lock) {
NamedCache nc = getNamedCache(cacheToken);
CacheEntry e = new CacheEntry();
e.request = cacheToken.request;
e.persistent = persistent;
e.v = res;
store(cacheToken, persistent, nc, e);
}
}
// persistence
public void save() {
}
private void save(NamedCache nc) {
if (folder == null)
return;
try {
FileWriter sw = new FileWriter(new File(Utilities.path(folder, nc.name+".cache")));
sw.write(ENTRY_MARKER+"\r\n");
JsonParser json = new JsonParser();
json.setOutputStyle(OutputStyle.PRETTY);
for (CacheEntry ce : nc.list) {
sw.write(ce.request);
sw.write(BREAK+"\r\n");
if (ce.e != null) {
sw.write("e: {\r\n");
if (ce.e.getValueset() != null)
sw.write(" \"valueSet\" : "+json.composeString(ce.e.getValueset())+",\r\n");
sw.write(" \"error\" : \""+Utilities.escapeJson(ce.e.getError())+"\"\r\n}\r\n");
} else {
sw.write("v: {\r\n");
sw.write(" \"display\" : \""+Utilities.escapeJson(ce.v.getDisplay())+"\",\r\n");
sw.write(" \"severity\" : "+(ce.v.getSeverity() == null ? "null" : "\""+ce.v.getSeverity().toCode()+"\"")+",\r\n");
sw.write(" \"error\" : \""+Utilities.escapeJson(ce.v.getMessage())+"\"\r\n}\r\n");
}
sw.write(ENTRY_MARKER+"\r\n");
}
sw.close();
} catch (Exception e) {
System.out.println("error saving "+nc.name+": "+e.getMessage());
}
}
private void load() throws FHIRException {
for (String fn : new File(folder).list()) {
if (fn.endsWith(".cache") && !fn.equals("validation.cache")) {
try {
// System.out.println("Load "+fn);
String title = fn.substring(0, fn.lastIndexOf("."));
NamedCache nc = new NamedCache();
nc.name = title;
caches.put(title, nc);
String src = TextFile.fileToString(Utilities.path(folder, fn));
int i = src.indexOf(ENTRY_MARKER);
while (i > -1) {
String s = src.substring(0, i);
src = src.substring(i+ENTRY_MARKER.length()+1);
i = src.indexOf(ENTRY_MARKER);
if (!Utilities.noString(s)) {
int j = s.indexOf(BREAK);
String q = s.substring(0, j);
String p = s.substring(j+BREAK.length()+1).trim();
CacheEntry ce = new CacheEntry();
ce.persistent = true;
ce.request = q;
boolean e = p.charAt(0) == 'e';
p = p.substring(3);
JsonObject o = (JsonObject) new com.google.gson.JsonParser().parse(p);
String error = loadJS(o.get("error"));
if (e) {
if (o.has("valueSet"))
ce.e = new ValueSetExpansionOutcome((ValueSet) new JsonParser().parse(o.getAsJsonObject("valueSet")), error, TerminologyServiceErrorClass.UNKNOWN);
else
ce.e = new ValueSetExpansionOutcome(error, TerminologyServiceErrorClass.UNKNOWN);
} else {
IssueSeverity severity = o.get("severity") instanceof JsonNull ? null : IssueSeverity.fromCode(o.get("severity").getAsString());
String display = loadJS(o.get("display"));
ce.v = new ValidationResult(severity, error, new ConceptDefinitionComponent().setDisplay(display));
}
nc.map.put(String.valueOf(hashNWS(ce.request)), ce);
nc.list.add(ce);
}
}
} catch (Exception e) {
throw new FHIRException("Error loading "+fn+": "+e.getMessage(), e);
}
}
}
}
private String loadJS(JsonElement e) {
if (e == null)
return null;
if (!(e instanceof JsonPrimitive))
return null;
String s = e.getAsString();
if ("".equals(s))
return null;
return s;
}
private String hashNWS(String s) {
return String.valueOf(s.replace("\r", "").replace("\n", "").replace(" ", "").hashCode());
}
// management
public TerminologyCache copy() {
// TODO Auto-generated method stub
return null;
}
public String summary(ValueSet vs) {
if (vs == null)
return "null";
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (ConceptSetComponent cc : vs.getCompose().getInclude())
b.append("Include "+getIncSummary(cc));
for (ConceptSetComponent cc : vs.getCompose().getExclude())
b.append("Exclude "+getIncSummary(cc));
return b.toString();
}
private String getIncSummary(ConceptSetComponent cc) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (UriType vs : cc.getValueSet())
b.append(vs.asStringValue());
String vsd = b.length() > 0 ? " where the codes are in the value sets ("+b.toString()+")" : "";
String system = cc.getSystem();
if (cc.hasConcept())
return Integer.toString(cc.getConcept().size())+" codes from "+system+vsd;
if (cc.hasFilter()) {
String s = "";
for (ConceptSetFilterComponent f : cc.getFilter()) {
if (!Utilities.noString(s))
s = s + " & ";
s = s + f.getProperty()+" "+f.getOp().toCode()+" "+f.getValue();
}
return "from "+system+" where "+s+vsd;
}
return "All codes from "+system+vsd;
}
public String summary(Coding code) {
return code.getSystem()+"#"+code.getCode()+": \""+code.getDisplay()+"\"";
}
public String summary(CodeableConcept code) {
StringBuilder b = new StringBuilder();
b.append("{");
boolean first = true;
for (Coding c : code.getCoding()) {
if (first) first = false; else b.append(",");
b.append(summary(c));
}
b.append("}: \"");
b.append(code.getText());
b.append("\"");
return b.toString();
}
}

View File

@ -21,6 +21,8 @@ import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -470,9 +472,30 @@ public class Element extends Base {
return this;
}
public void markValidation(StructureDefinition profile, ElementDefinition definition) {
public void clearDecorations() {
clearUserData("fhir.decorations");
for (Element e : children)
e.clearDecorations();
}
public void markValidation(StructureDefinition profile, ElementDefinition definition) {
@SuppressWarnings("unchecked")
List<ElementDecoration> decorations = (List<ElementDecoration>) getUserData("fhir.decorations");
if (decorations == null) {
decorations = new ArrayList<ElementDecoration>();
setUserData("fhir.decorations", decorations);
}
decorations.add(new ElementDecoration(DecorationType.TYPE, profile.getUserString("path"), definition.getPath()));
if (tail(definition.getId()).contains(":")) {
String[] details = tail(definition.getId()).split("\\:");
decorations.add(new ElementDecoration(DecorationType.SLICE, null, details[1]));
}
}
private String tail(String id) {
return id.contains(".") ? id.substring(id.lastIndexOf(".")+1) : id;
}
public Element getNamedChild(String name) {
if (children == null)
return null;
@ -660,7 +683,7 @@ public class Element extends Base {
private List<ElementDefinition> children;
public ElementSortComparator(Element e, Property property) {
String tn = e.getType();
StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn));
StructureDefinition sd = property.getContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn, property.getContext().getOverrideVersionNs()));
if (sd != null && !sd.getAbstract())
children = sd.getSnapshot().getElement();
else

View File

@ -80,7 +80,7 @@ public class JsonParser extends ParserBase {
assert (map.containsKey(obj));
return parse(obj);
} else {
JsonObject obj = (JsonObject) new com.google.gson.JsonParser().parse(source);
JsonObject obj = JsonTrackingParser.parse(source, null); // (JsonObject) new com.google.gson.JsonParser().parse(source);
// assert (map.containsKey(obj));
return parse(obj);
}
@ -284,7 +284,7 @@ public class JsonParser extends ParserBase {
logError(line(res), col(res), npath, IssueType.INVALID, "Unable to find resourceType property", IssueSeverity.FATAL);
} else {
String name = rt.getAsString();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name));
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs()));
if (sd == null)
throw new FHIRFormatError("Contained resource does not appear to be a FHIR resource (unknown name '"+name+"')");
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
@ -447,7 +447,11 @@ public class JsonParser extends ParserBase {
else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt"))
json.value(new Integer(item.getValue()));
else if (Utilities.existsInList(type, "decimal"))
json.value(new BigDecimal(item.getValue()));
try {
json.value(new BigDecimal(item.getValue()));
} catch (Exception e) {
throw new NumberFormatException("error writing number '"+item.getValue()+"' to JSON");
}
else
json.value(item.getValue());
}

View File

@ -42,7 +42,7 @@ public class ObjectConverter {
if (base == null)
return null;
String tn = base.fhirType();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn));
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(tn, context.getOverrideVersionNs()));
if (sd == null)
throw new FHIRException("Unable to find definition for type "+tn);
Element res = new Element(property.getName(), property);
@ -86,7 +86,7 @@ public class ObjectConverter {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
try {
new JsonParser(context).compose(element, bo, OutputStyle.NORMAL, null);
TextFile.bytesToFile(bo.toByteArray(), "c:\\temp\\json.json");
// TextFile.bytesToFile(bo.toByteArray(), "c:\\temp\\json.json");
return new org.hl7.fhir.r4.formats.JsonParser().parse(bo.toByteArray());
} catch (IOException e) {
// won't happen

View File

@ -3,13 +3,16 @@ package org.hl7.fhir.r4.elementmodel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.formats.FormatUtilities;
import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
@ -33,7 +36,7 @@ public abstract class ParserBase {
public boolean isPrimitive(String code) {
return Utilities.existsInList(code, "boolean", "integer", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml", "url", "canonical");
// StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code);
// StructureDefinition sd = context.fetchTypeDefinition(code);
// return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
@ -41,7 +44,8 @@ public abstract class ParserBase {
protected ValidationPolicy policy;
protected List<ValidationMessage> errors;
protected ILinkResolver linkResolver;
protected boolean showDecorations;
public ParserBase(IWorkerContext context) {
super();
this.context = context;
@ -77,7 +81,7 @@ public abstract class ParserBase {
return null;
}
for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getIdElement().getIdPart()) && !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/de-")) {
if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/de-")) {
if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return sd;
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
@ -101,7 +105,7 @@ public abstract class ParserBase {
}
}
for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getIdElement().getIdPart())) {
if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
return sd;
}
}
@ -118,7 +122,13 @@ public abstract class ParserBase {
return this;
}
public boolean isShowDecorations() {
return showDecorations;
}
public void setShowDecorations(boolean showDecorations) {
this.showDecorations = showDecorations;
}
}

View File

@ -13,6 +13,7 @@ import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.r4.utils.TypesUtilities;
import org.hl7.fhir.utilities.Utilities;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.DefinitionException;
@ -88,7 +89,10 @@ public class Property {
} else
throw new Error("logic error, gettype when types > 1, name mismatch for "+elementName+" on at "+ed.getPath());
} else if (ed.getType().get(0).getCode() == null) {
return structure.getId();
if (Utilities.existsInList(ed.getId(), "Element.id", "Extension.url"))
return "string";
else
return structure.getId();
} else
return ed.getType().get(0).getCode();
}
@ -135,8 +139,10 @@ public class Property {
* @param E.g. "integer"
*/
public boolean isPrimitive(String code) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
return TypesUtilities.isPrimitive(code);
// was this... but this can be very inefficient compared to hard coding the list
// StructureDefinition sd = context.fetchTypeDefinition(code);
// return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
private String lowFirst(String t) {
@ -191,7 +197,7 @@ public class Property {
return false;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name));
if (sd == null)
sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(getType(name)));
sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(getType(name), context.getOverrideVersionNs()));
if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE)
return true;
if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL)
@ -274,7 +280,7 @@ public class Property {
assert aType.getProfile().size() == 1;
url = aType.getProfile().get(0).getValue();
} else {
url = ProfileUtilities.sdNs(t);
url = ProfileUtilities.sdNs(t, context.getOverrideVersionNs());
}
break;
}

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.r4.elementmodel;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -7,8 +8,10 @@ import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import javax.sql.rowset.spi.XmlWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
@ -33,9 +36,12 @@ import org.hl7.fhir.r4.utils.formats.XmlLocationData;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.xhtml.CDANarrativeFormat;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
@ -47,6 +53,8 @@ import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import com.sun.webkit.ContextMenu.ShowContext;
public class XmlParser extends ParserBase {
private boolean allowXsiLocation;
@ -63,7 +71,6 @@ public class XmlParser extends ParserBase {
this.allowXsiLocation = allowXsiLocation;
}
public Element parse(InputStream stream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
Document doc = null;
try {
@ -115,7 +122,7 @@ public class XmlParser extends ParserBase {
}
private void checkForProcessingInstruction(Document document) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
if (policy == ValidationPolicy.EVERYTHING && FormatUtilities.FHIR_NS.equals(document.getDocumentElement().getNamespaceURI())) {
Node node = document.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE)
@ -192,7 +199,7 @@ public class XmlParser extends ParserBase {
private void checkElement(org.w3c.dom.Element element, String path, Property prop) throws FHIRFormatError {
if (policy == ValidationPolicy.EVERYTHING) {
if (empty(element))
if (empty(element) && FormatUtilities.FHIR_NS.equals(element.getNamespaceURI())) // this rule only applies to FHIR Content
logError(line(element), col(element), path, IssueType.INVALID, "Element must have some content", IssueSeverity.ERROR);
String ns = FormatUtilities.FHIR_NS;
if (ToolingExtensions.hasExtension(prop.getDefinition(), "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
@ -237,13 +244,22 @@ public class XmlParser extends ParserBase {
if (property != null) {
String av = attr.getNodeValue();
if (ToolingExtensions.hasExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"))
av = convertForDateFormat(ToolingExtensions.readStringExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av);
av = convertForDateFormatFromExternal(ToolingExtensions.readStringExtension(property.getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av);
if (property.getName().equals("value") && context.isPrimitive())
context.setValue(av);
else
context.getChildren().add(new Element(property.getName(), property, property.getType(), av).markLocation(line(node), col(node)));
} else if (!allowXsiLocation || !attr.getNodeName().endsWith(":schemaLocation") ) {
logError(line(node), col(node), path, IssueType.STRUCTURE, "Undefined attribute '@"+attr.getNodeName()+"' on "+node.getNodeName()+" for type "+context.fhirType()+" (properties = "+properties+")", IssueSeverity.ERROR);
} else {
boolean ok = false;
if (FormatUtilities.FHIR_NS.equals(node.getNamespaceURI())) {
if (attr.getLocalName().equals("schemaLocation") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())) {
ok = ok || allowXsiLocation;
}
} else
ok = ok || (attr.getLocalName().equals("schemaLocation")); // xsi:schemalocation allowed for non FHIR content
ok = ok || (hasTypeAttr(context) && attr.getLocalName().equals("type") && FormatUtilities.NS_XSI.equals(attr.getNamespaceURI())); // xsi:type allowed if element says so
if (!ok)
logError(line(node), col(node), path, IssueType.STRUCTURE, "Undefined attribute '@"+attr.getNodeName()+"' on "+node.getNodeName()+" for type "+context.fhirType()+" (properties = "+properties+")", IssueSeverity.ERROR);
}
}
}
@ -254,8 +270,12 @@ public class XmlParser extends ParserBase {
Property property = getElementProp(properties, child.getLocalName());
if (property != null) {
if (!property.isChoice() && "xhtml".equals(property.getType())) {
XhtmlNode xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child);
context.getChildren().add(new Element("div", property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child), col(child)));
XhtmlNode xhtml;
if (property.getDefinition().hasRepresentation(PropertyRepresentation.CDATEXT))
xhtml = new CDANarrativeFormat().convert((org.w3c.dom.Element) child);
else
xhtml = new XhtmlParser().setValidatorMode(true).parseHtmlNode((org.w3c.dom.Element) child);
context.getChildren().add(new Element(property.getName(), property, "xhtml", new XhtmlComposer(XhtmlComposer.XML, false).compose(xhtml)).setXhtml(xhtml).markLocation(line(child), col(child)));
} else {
String npath = path+"/"+pathPrefix(child.getNamespaceURI())+child.getLocalName();
Element n = new Element(child.getLocalName(), property).markLocation(line(child), col(child));
@ -328,7 +348,7 @@ public class XmlParser extends ParserBase {
return null;
}
private String convertForDateFormat(String fmt, String av) throws FHIRException {
private String convertForDateFormatFromExternal(String fmt, String av) throws FHIRException {
if ("v3".equals(fmt)) {
DateTimeType d = DateTimeType.parseV3(av);
return d.asStringValue();
@ -336,10 +356,18 @@ public class XmlParser extends ParserBase {
throw new FHIRException("Unknown Data format '"+fmt+"'");
}
private String convertForDateFormatToExternal(String fmt, String av) throws FHIRException {
if ("v3".equals(fmt)) {
DateTimeType d = new DateTimeType(av);
return d.getAsV3();
} else
throw new FHIRException("Unknown Data format '"+fmt+"'");
}
private void parseResource(String string, org.w3c.dom.Element container, Element parent, Property elementProperty) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
org.w3c.dom.Element res = XMLUtil.getFirstChild(container);
String name = res.getLocalName();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name));
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, context.getOverrideVersionNs()));
if (sd == null)
throw new FHIRFormatError("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);
@ -365,14 +393,32 @@ public class XmlParser extends ParserBase {
}
}
private boolean isAttr(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.XMLATTR) {
return true;
}
}
return false;
}
private boolean isAttr(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.XMLATTR) {
return true;
}
}
return false;
}
private boolean isCdaText(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.CDATEXT) {
return true;
}
}
return false;
}
private boolean isTypeAttr(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
if (r.getValue() == PropertyRepresentation.TYPEATTR) {
return true;
}
}
return false;
}
private boolean isText(Property property) {
for (Enumeration<PropertyRepresentation> r : property.getDefinition().getRepresentation()) {
@ -384,16 +430,30 @@ public class XmlParser extends ParserBase {
}
@Override
public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException {
XMLWriter xml = new XMLWriter(stream, "UTF-8");
public void compose(Element e, OutputStream stream, OutputStyle style, String base) throws IOException, FHIRException {
XMLWriter xml = new XMLWriter(stream, "UTF-8");
xml.setSortAttributes(false);
xml.setPretty(style == OutputStyle.PRETTY);
xml.start();
xml.setDefaultNamespace(e.getProperty().getNamespace());
if (hasTypeAttr(e))
xml.namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi");
composeElement(xml, e, e.getType(), true);
xml.end();
}
private boolean hasTypeAttr(Element e) {
if (isTypeAttr(e.getProperty()))
return true;
for (Element c : e.getChildren()) {
if (hasTypeAttr(c))
return true;
}
return false;
}
public void compose(Element e, IXMLWriter xml) throws Exception {
xml.start();
xml.setDefaultNamespace(e.getProperty().getNamespace());
@ -401,7 +461,14 @@ public class XmlParser extends ParserBase {
xml.end();
}
private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException {
private void composeElement(IXMLWriter xml, Element element, String elementName, boolean root) throws IOException, FHIRException {
if (showDecorations) {
@SuppressWarnings("unchecked")
List<ElementDecoration> decorations = (List<ElementDecoration>) element.getUserData("fhir.decorations");
if (decorations != null)
for (ElementDecoration d : decorations)
xml.decorate(d);
}
for (String s : element.getComments()) {
xml.comment(s, true);
}
@ -413,12 +480,19 @@ public class XmlParser extends ParserBase {
xml.exit(elementName);
} else if (element.isPrimitive() || (element.hasType() && isPrimitive(element.getType()))) {
if (element.getType().equals("xhtml")) {
xml.escapedText(element.getValue());
String rawXhtml = element.getValue();
if (isCdaText(element.getProperty())) {
new CDANarrativeFormat().convert(xml, element.getXhtml());
} else
xml.escapedText(rawXhtml);
} else if (isText(element.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveProperty(element.getProperty()));
xml.text(element.getValue());
} else {
if (isTypeAttr(element.getProperty()) && !Utilities.noString(element.getType())) {
xml.attribute("xsi:type", element.getType());
}
if (element.hasValue()) {
if (linkResolver != null)
xml.link(linkResolver.resolveType(element.getType()));
@ -432,14 +506,20 @@ public class XmlParser extends ParserBase {
composeElement(xml, child, child.getName(), false);
xml.exit(elementName);
} else
xml.element(elementName);
xml.element(elementName);
}
} else {
if (isTypeAttr(element.getProperty()) && !Utilities.noString(element.getType())) {
xml.attribute("xsi:type", element.getType());
}
for (Element child : element.getChildren()) {
if (isAttr(child.getProperty())) {
if (linkResolver != null)
xml.link(linkResolver.resolveType(child.getType()));
xml.attribute(child.getName(), child.getValue());
String av = child.getValue();
if (ToolingExtensions.hasExtension(child.getProperty().getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"))
av = convertForDateFormatToExternal(ToolingExtensions.readStringExtension(child.getProperty().getDefinition(), "http://www.healthintersections.com.au/fhir/StructureDefinition/elementdefinition-dateformat"), av);
xml.attribute(child.getName(), av);
}
}
if (linkResolver != null)

View File

@ -26,6 +26,7 @@ public interface JsonCreator {
void value(Boolean value) throws IOException;
void value(BigDecimal value) throws IOException;
void valueNum(String value) throws IOException; // allow full control of representation
void value(Integer value) throws IOException;

View File

@ -8,8 +8,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Stack;
import com.google.gson.stream.JsonWriter;
public class JsonCreatorCanonical implements JsonCreator {
public class JsonCanValue {
@ -27,6 +25,14 @@ public class JsonCreatorCanonical implements JsonCreator {
}
}
private class JsonCanPresentedNumberValue extends JsonCanValue {
private String value;
private JsonCanPresentedNumberValue(String name, String value) {
super(name);
this.value = value;
}
}
private class JsonCanIntegerValue extends JsonCanValue {
private Integer value;
private JsonCanIntegerValue(String name, Integer value) {
@ -74,12 +80,12 @@ public class JsonCreatorCanonical implements JsonCreator {
Stack<JsonCanObject> stack;
JsonCanObject root;
JsonWriter gson;
JsonCreatorDirect jj;
String name;
public JsonCreatorCanonical(OutputStreamWriter osw) {
stack = new Stack<JsonCreatorCanonical.JsonCanObject>();
gson = new JsonWriter(osw);
jj = new JsonCreatorDirect(osw);
name = null;
}
@ -93,7 +99,7 @@ public class JsonCreatorCanonical implements JsonCreator {
public void setIndent(String indent) {
if (!indent.equals(""))
throw new Error("do not use pretty when canonical is set");
gson.setIndent(indent);
jj.setIndent(indent);
}
@Override
@ -135,6 +141,11 @@ public class JsonCreatorCanonical implements JsonCreator {
public void value(BigDecimal value) throws IOException {
stack.peek().addProp(new JsonCanNumberValue(takeName(), value));
}
@Override
public void valueNum(String value) throws IOException {
stack.peek().addProp(new JsonCanPresentedNumberValue(takeName(), value));
}
@Override
public void value(Integer value) throws IOException {
@ -161,24 +172,26 @@ public class JsonCreatorCanonical implements JsonCreator {
}
private void writeObject(JsonCanObject obj) throws IOException {
gson.beginObject();
jj.beginObject();
List<String> names = new ArrayList<String>();
for (JsonCanValue v : obj.children)
names.add(v.name);
Collections.sort(names);
for (String n : names) {
gson.name(n);
jj.name(n);
JsonCanValue v = getPropForName(n, obj.children);
if (v instanceof JsonCanNumberValue)
gson.value(((JsonCanNumberValue) v).value);
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanPresentedNumberValue)
jj.valueNum(((JsonCanPresentedNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
gson.value(((JsonCanIntegerValue) v).value);
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
gson.value(((JsonCanBooleanValue) v).value);
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
gson.value(((JsonCanStringValue) v).value);
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
gson.nullValue();
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
@ -188,7 +201,7 @@ public class JsonCreatorCanonical implements JsonCreator {
} else
throw new Error("not possible");
}
gson.endObject();
jj.endObject();
}
private JsonCanValue getPropForName(String name, List<JsonCanValue> children) {
@ -199,18 +212,18 @@ public class JsonCreatorCanonical implements JsonCreator {
}
private void writeArray(JsonCanObject arr) throws IOException {
gson.beginArray();
jj.beginArray();
for (JsonCanValue v : arr.children) {
if (v instanceof JsonCanNumberValue)
gson.value(((JsonCanNumberValue) v).value);
jj.value(((JsonCanNumberValue) v).value);
else if (v instanceof JsonCanIntegerValue)
gson.value(((JsonCanIntegerValue) v).value);
jj.value(((JsonCanIntegerValue) v).value);
else if (v instanceof JsonCanBooleanValue)
gson.value(((JsonCanBooleanValue) v).value);
jj.value(((JsonCanBooleanValue) v).value);
else if (v instanceof JsonCanStringValue)
gson.value(((JsonCanStringValue) v).value);
jj.value(((JsonCanStringValue) v).value);
else if (v instanceof JsonCanNullValue)
gson.nullValue();
jj.nullValue();
else if (v instanceof JsonCanObject) {
JsonCanObject o = (JsonCanObject) v;
if (o.array)
@ -220,7 +233,7 @@ public class JsonCreatorCanonical implements JsonCreator {
} else
throw new Error("not possible");
}
gson.endArray();
jj.endArray();
}
@Override

View File

@ -0,0 +1,172 @@
package org.hl7.fhir.r4.formats;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.Stack;
import org.hl7.fhir.utilities.Utilities;
/**
* A little implementation of a json write to replace Gson .... because Gson screws up decimal values, and *we care*
*
* @author Grahame Grieve
*
*/
public class JsonCreatorDirect implements JsonCreator {
private Writer writer;
private boolean pretty;
private boolean named;
private boolean valued;
private int indent;
public JsonCreatorDirect(Writer writer) {
super();
this.writer = writer;
}
@Override
public void setIndent(String indent) {
this.pretty = !Utilities.noString(indent);
}
@Override
public void beginObject() throws IOException {
checkState();
writer.write("{");
stepIn();
}
public void stepIn() throws IOException {
if (pretty) {
indent++;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
public void stepOut() throws IOException {
if (pretty) {
indent--;
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
}
private void checkState() throws IOException {
if (named) {
if (pretty)
writer.write(" : ");
else
writer.write(":");
named = false;
}
if (valued) {
writer.write(",");
if (pretty) {
writer.write("\r\n");
for (int i = 0; i < indent; i++) {
writer.write(" ");
}
}
valued = false;
}
}
@Override
public void endObject() throws IOException {
stepOut();
writer.write("}");
}
@Override
public void nullValue() throws IOException {
checkState();
writer.write("null");
valued = true;
}
@Override
public void name(String name) throws IOException {
checkState();
writer.write("\""+name+"\"");
named = true;
}
@Override
public void value(String value) throws IOException {
checkState();
writer.write("\""+Utilities.escapeJson(value)+"\"");
valued = true;
}
@Override
public void value(Boolean value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else if (value.booleanValue())
writer.write("true");
else
writer.write("false");
valued = true;
}
@Override
public void value(BigDecimal value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued = true;
}
@Override
public void valueNum(String value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value);
valued = true;
}
@Override
public void value(Integer value) throws IOException {
checkState();
if (value == null)
writer.write("null");
else
writer.write(value.toString());
valued = true;
}
@Override
public void beginArray() throws IOException {
checkState();
writer.write("[");
}
@Override
public void endArray() throws IOException {
writer.write("]");
}
@Override
public void finish() throws IOException {
// nothing
}
@Override
public void link(String href) {
// not used
}
}

View File

@ -80,4 +80,9 @@ public class JsonCreatorGson implements JsonCreator {
// not used
}
@Override
public void valueNum(String value) throws IOException {
value(new BigDecimal(value));
}
}

Some files were not shown because too many files have changed in this diff Show More