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) { } else if (thePaths instanceof HashSet) {
myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>) thePaths).clone(); myDontStripVersionsFromReferencesAtPaths = (Set<String>) ((HashSet<String>) thePaths).clone();
} else { } else {
myDontStripVersionsFromReferencesAtPaths = new HashSet<String>(thePaths); myDontStripVersionsFromReferencesAtPaths = new HashSet<>(thePaths);
} }
return this; return this;
} }

View File

@ -201,8 +201,10 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
* SPs with the same path the same way. This behaviour is * SPs with the same path the same way. This behaviour is
* used by AuthorizationInterceptor * used by AuthorizationInterceptor
*/ */
String nextPath = massagePathForCompartmentSimilarity(next.getPath());
for (RuntimeSearchParam nextAlternate : searchParams) { 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())) { if (!nextAlternate.getName().equals(next.getName())) {
searchParamsForCompartment.add(nextAlternate); 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 @Deprecated
public synchronized IBaseResource toProfile() { public synchronized IBaseResource toProfile() {
validateSealed(); validateSealed();

View File

@ -469,7 +469,7 @@ public abstract class BaseParser implements IParser {
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(theIResource); TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(theIResource);
if (shouldAddSubsettedTag()) { if (shouldAddSubsettedTag()) {
tags = new TagList(tags); 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; return tags;
@ -746,7 +746,7 @@ public abstract class BaseParser implements IParser {
if (shouldAddSubsettedTag()) { if (shouldAddSubsettedTag()) {
IBaseCoding coding = metaValue.addTag(); IBaseCoding coding = metaValue.addTag();
coding.setCode(Constants.TAG_SUBSETTED_CODE); coding.setCode(Constants.TAG_SUBSETTED_CODE);
coding.setSystem(Constants.TAG_SUBSETTED_SYSTEM); coding.setSystem(getSubsettedCodeSystem());
coding.setDisplay(subsetDescription()); coding.setDisplay(subsetDescription());
} }
@ -785,6 +785,14 @@ public abstract class BaseParser implements IParser {
return retVal; 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 @Override
public void setDontEncodeElements(Set<String> theDontEncodeElements) { public void setDontEncodeElements(Set<String> theDontEncodeElements) {
myDontEncodeElementsIncludesStars = false; myDontEncodeElementsIncludesStars = false;

View File

@ -1286,9 +1286,7 @@ class ParserState<T> {
} else { } else {
try { try {
myInstance.setValueAsString(theValue); myInstance.setValueAsString(theValue);
} catch (DataFormatException e) { } catch (DataFormatException | IllegalArgumentException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage());
} catch (IllegalArgumentException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage()); 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_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501; public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String TAG_SUBSETTED_CODE = "SUBSETTED"; 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_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata"; public static final String URL_TOKEN_METADATA = "metadata";
public static final String OO_INFOSTATUS_PROCESSING = "processing"; 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"), 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: * 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 * The underlying cause exception
*/ */
public BaseServerResponseException(int theStatusCode, Throwable theCause) { public BaseServerResponseException(int theStatusCode, Throwable theCause) {
super(theCause.toString(), theCause); super(theCause.getMessage(), theCause);
myStatusCode = theStatusCode; myStatusCode = theStatusCode;
myBaseOperationOutcome = null; 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; public static final int STATUS_CODE = Constants.STATUS_HTTP_400_BAD_REQUEST;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* Constructor
*/
public InvalidRequestException(String theMessage) { public InvalidRequestException(String theMessage) {
super(STATUS_CODE, theMessage); super(STATUS_CODE, theMessage);
} }
/**
* Constructor
*/
public InvalidRequestException(String theMessage, Throwable theCause) {
super(STATUS_CODE, theMessage, theCause);
}
/**
* Constructor
*/
public InvalidRequestException(Throwable theCause) { public InvalidRequestException(Throwable theCause) {
super(STATUS_CODE, 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.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
* #%L * #%L
@ -24,6 +25,8 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
*/ */
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.*;
@ -40,6 +43,7 @@ import ca.uhn.fhir.parser.DataFormatException;
public class FhirTerser { public class FhirTerser {
public static final Pattern COMPARTMENT_MATCHER_PATH = Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
private FhirContext myContext; private FhirContext myContext;
public FhirTerser(FhirContext theContext) { public FhirTerser(FhirContext theContext) {
@ -364,8 +368,26 @@ public class FhirTerser {
List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName); List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName);
for (RuntimeSearchParam nextParam : params) { for (RuntimeSearchParam nextParam : params) {
for (String nextPath : nextParam.getPathsSplit()) { 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)) { 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 * 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) { if (isBlank(nextRef) && nextValue.getResource() != null) {
IBaseResource nextTarget = nextValue.getResource(); IBaseResource nextTarget = nextValue.getResource();
IIdType nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless(); nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
if (!nextTargetId.hasResourceType()) { if (!nextTargetId.hasResourceType()) {
String resourceType = myContext.getResourceDefinition(nextTarget).getName(); String resourceType = myContext.getResourceDefinition(nextTarget).getName();
nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null); nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null);
@ -382,6 +404,12 @@ public class FhirTerser {
nextRef = nextTargetId.getValue(); nextRef = nextTargetId.getValue();
} }
if (isNotBlank(wantType)) {
if (!nextTargetId.getResourceType().equals(wantType)) {
continue;
}
}
if (wantRef.equals(nextRef)) { if (wantRef.equals(nextRef)) {
return true; 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.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; 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.Collections;
import java.util.List; import java.util.List;
@ -39,7 +39,7 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
private FhirContext myCtx = FhirContext.forR4(); private FhirContext myCtx = FhirContext.forR4();
@Override @Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null; return null;
} }

View File

@ -98,9 +98,9 @@ public class VersionedApiConverterInterceptor extends InterceptorAdapter {
IBaseResource converted = null; IBaseResource converted = null;
try { try {
if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) { 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) { } 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) { } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) {
converted = myVersionConvertor_10_40.convertResource(toR4(responseResource)); converted = myVersionConvertor_10_40.convertResource(toR4(responseResource));
} else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) { } else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) {

View File

@ -1,28 +1,69 @@
package org.hl7.fhir.convertors; package org.hl7.fhir.convertors;
/*- import org.hl7.fhir.utilities.Utilities;
* #%L
* HAPI FHIR - Converter public class VersionConvertorConstants {
* %%
* Copyright (C) 2014 - 2018 University Health Network 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";
* Licensed under the Apache License, Version 2.0 (the "License"); public final static String MODIFIER_REASON_EXTENSION = "http://hl7.org/fhir/4.0/StructureDefinition/extension-ElementDefinition.isModifierReason";
* you may not use this file except in compliance with the License. public final static String MODIFIER_REASON_LEGACY = "No Modifier Reason provideed in previous versions of FHIR";
* You may obtain a copy of the License at
* public static String refToVS(String url) {
* http://www.apache.org/licenses/LICENSE-2.0 if (url == null)
* return null;
* Unless required by applicable law or agreed to in writing, software if (url.equals("http://www.genenames.org"))
* distributed under the License is distributed on an "AS IS" BASIS, return "http://hl7.org/fhir/ValueSet/genenames";
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. else if (url.equals("http://varnomen.hgvs.org/"))
* See the License for the specific language governing permissions and return "http://hl7.org/fhir/ValueSet/variants";
* limitations under the License. else if (url.equals("http://www.ncbi.nlm.nih.gov/nuccore?db=nuccore"))
* #L% 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"))
public class VersionConvertorConstants { return "http://hl7.org/fhir/ValueSet/clinvar";
else if (url.equals("http://cancer.sanger.ac.uk/cancergenome/projects/cosmic/"))
public final static String MODIFIER_REASON_EXTENSION = "http://hl7.org/fhir/tooling/StructureDefinition/r4ModifierReason"; return "http://hl7.org/fhir/ValueSet/cosmic";
public final static String MODIFIER_REASON_LEGACY = "No Modifier Reason provideed in previous versions of FHIR"; 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 // Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions(); ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference"); parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.what");
return retVal; 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); ResourceTable resource = myResourceTableDao.findById(theResourceId).orElseThrow(IllegalStateException::new);
ResourceHistoryTable currentVersion = myResourceHistoryTableDao.findForIdAndVersion(resource.getId(), resource.getVersion()); 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()); 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; IResource res = (IResource) theResource;
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res); TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
if (tagList != null) { 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(); totalMetaCount += tagList.size();
} }
List<IdDt> profileList = ResourceMetadataKeyEnum.PROFILES.get(res); List<IdDt> profileList = ResourceMetadataKeyEnum.PROFILES.get(res);
@ -2347,7 +2349,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
} }
} else { } else {
IAnyResource res = (IAnyResource) theResource; 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().getTag().size();
totalMetaCount += res.getMeta().getProfile().size(); totalMetaCount += res.getMeta().getProfile().size();
totalMetaCount += res.getMeta().getSecurity().size(); totalMetaCount += res.getMeta().getSecurity().size();

View File

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

View File

@ -1186,6 +1186,10 @@ public class SearchBuilder implements ISearchBuilder {
BaseCodingDt id = (BaseCodingDt) theParameter; BaseCodingDt id = (BaseCodingDt) theParameter;
system = id.getSystemElement().getValueAsString(); system = id.getSystemElement().getValueAsString();
code = (id.getCodeElement().getValue()); code = (id.getCodeElement().getValue());
} else if (theParameter instanceof NumberParam) {
NumberParam number = (NumberParam) theParameter;
system = null;
code = number.getValueAsQueryToken(myContext);
} else { } else {
throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass()); throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass());
} }
@ -2082,11 +2086,16 @@ public class SearchBuilder implements ISearchBuilder {
* SearchParameters can declare paths on multiple resources * SearchParameters can declare paths on multiple resources
* types. Here we only want the ones that actually apply. * types. Here we only want the ones that actually apply.
*/ */
for (Iterator<String> iter = path.iterator(); iter.hasNext(); ) { path = new ArrayList<>(path);
if (!iter.next().startsWith(theResourceType + ".")) {
iter.remove(); 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); 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.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; 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.BeansException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -66,7 +66,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
@Override @Override
@Transactional(value = TxType.SUPPORTS) @Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) { public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
return null; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -31,6 +31,7 @@ import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException; 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.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -48,14 +49,16 @@ import javax.measure.unit.NonSI;
import javax.measure.unit.Unit; import javax.measure.unit.Unit;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; 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.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor { public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR4.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR4.class);
@Autowired @Autowired
private org.hl7.fhir.r4.hapi.ctx.IValidationSupport myValidationSupport; 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) { public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
ArrayList<PathAndRef> retVal = new ArrayList<>(); ArrayList<PathAndRef> retVal = new ArrayList<>();
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath()); String[] nextPathsSplit = SPLIT_R4.split(theNextSpDef.getPath());
for (String path : nextPathsSplit) { for (String path : nextPathsSplit) {
path = path.trim(); path = path.trim();
if (isNotBlank(path)) { if (isNotBlank(path)) {
for (Object next : extractValues(path, theResource)) { for (Object next : extractValues(path, theResource)) {
retVal.add(new PathAndRef(path, next)); retVal.add(new PathAndRef(path, next));
} }
@ -447,7 +451,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString()); addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
} }
} else if (nextObject instanceof Range) { } else if (nextObject instanceof Range) {
SimpleQuantity low = ((Range) nextObject).getLow(); Quantity low = ((Range) nextObject).getLow();
if (low != null) { if (low != null) {
BigDecimal value = low.getValue(); BigDecimal value = low.getValue();
if (value != null) { if (value != null) {
@ -708,9 +712,10 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
protected List<Object> extractValues(String thePaths, IBaseResource theResource) { protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
IWorkerContext worker = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport); IWorkerContext worker = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker); FHIRPathEngine fp = new FHIRPathEngine(worker);
fp.setHostServices(new SearchParamExtractorR4HostServices());
List<Object> values = new ArrayList<>(); List<Object> values = new ArrayList<>();
String[] nextPathsSplit = SPLIT.split(thePaths); String[] nextPathsSplit = SPLIT_R4.split(thePaths);
for (String nextPath : nextPathsSplit) { for (String nextPath : nextPathsSplit) {
List<Base> allValues; List<Base> allValues;
try { try {
@ -748,4 +753,88 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
return null; 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; private Long myHashIdentity;
public ResourceIndexedSearchParamQuantity() { public ResourceIndexedSearchParamQuantity() {
// nothing super();
} }
public ResourceIndexedSearchParamQuantity(String theParamName, BigDecimal theValue, String theSystem, String theUnits) { public ResourceIndexedSearchParamQuantity(String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
this();
setParamName(theParamName); setParamName(theParamName);
setSystem(theSystem); setSystem(theSystem);
setValue(theValue); setValue(theValue);

View File

@ -39,6 +39,7 @@ import java.util.Date;
public class ResourceLink implements Serializable { public class ResourceLink implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final int SRC_PATH_LENGTH = 200;
@SequenceGenerator(name = "SEQ_RESLINK_ID", sequenceName = "SEQ_RESLINK_ID") @SequenceGenerator(name = "SEQ_RESLINK_ID", sequenceName = "SEQ_RESLINK_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESLINK_ID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESLINK_ID")
@ -46,7 +47,7 @@ public class ResourceLink implements Serializable {
@Column(name = "PID") @Column(name = "PID")
private Long myId; private Long myId;
@Column(name = "SRC_PATH", length = 100, nullable = false) @Column(name = "SRC_PATH", length = SRC_PATH_LENGTH, nullable = false)
private String mySourcePath; private String mySourcePath;
@ManyToOne(optional = false, fetch = FetchType.LAZY) @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.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -142,10 +142,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override @Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSet valueSetToExpand = new ValueSet(); ValueSet valueSetToExpand = new ValueSet();
valueSetToExpand.getCompose().addInclude(theInclude); valueSetToExpand.getCompose().addInclude(theInclude);
return super.expandValueSet(valueSetToExpand).getExpansion(); ValueSet expanded = super.expandValueSet(valueSetToExpand);
return new ValueSetExpander.ValueSetExpansionOutcome(expanded);
} }
@Override @Override

View File

@ -385,7 +385,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
p.addName().addFamily("Hello"); p.addName().addFamily("Hello");
TagList tl = new TagList(); 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); ResourceMetadataKeyEnum.TAG_LIST.put(p, tl);
try { try {

View File

@ -653,7 +653,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails"); p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().setFamily("Hello"); 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 { try {
myPatientDao.create(p, mySrd); 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.hamcrest.Matchers.*;
import static org.junit.Assert.*; 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.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;

View File

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

View File

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

View File

@ -35,6 +35,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@SuppressWarnings("Duplicates")
public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; 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); validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult); ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) { assertEquals(1, result.getMessages().size());
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome())); assertThat(result.getMessages().get(0).getMessage(), containsString("dom-6: A resource should have narrative for robust management"));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
} }
@Test @Test
@ -1150,7 +1150,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless(); IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeIn2 = new AuditEvent(); 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(); IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeOut1 = new AuditEvent(); AuditEvent aeOut1 = new AuditEvent();
@ -1223,7 +1223,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
// Now let's update // Now let's update
valueSet = new ValueSet(); valueSet = new ValueSet();
valueSet.setId(vsid); 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); valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless(); 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.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().setFamily("Hello"); 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 { try {
myPatientDao.create(p, mySrd); myPatientDao.create(p, mySrd);

View File

@ -721,7 +721,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.setSubject(new Reference(ptid)); obs.setSubject(new Reference(ptid));
obs.setContext(new Reference(encid)); obs.setEncounter(new Reference(encid));
myObservationDao.create(obs); myObservationDao.create(obs);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { 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.getMeta().getProfile().add(new CanonicalType(sd.getUrl()));
input.addIdentifier().setSystem("http://acme").setValue("12345"); 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.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345"); 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.getMeta().getProfile().add(new CanonicalType(profileUri));
input.addIdentifier().setSystem("http://acme").setValue("12345"); 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.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345"); input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");

View File

@ -228,15 +228,17 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
IPrimitiveType<String> display = null; IPrimitiveType<String> display = null;
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/v2-0487"); StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/yesnodontknow");
StringType code = new StringType("BRN"); StringType code = new StringType("Y");
StringType system = new StringType("http://hl7.org/fhir/v2/0487"); StringType system = new StringType("http://terminology.hl7.org/CodeSystem/v2-0136");
ValidateCodeResult result = myValueSetDao.validateCode(vsIdentifier, null, code, system, display, coding, codeableConcept, mySrd); ValidateCodeResult result = myValueSetDao.validateCode(vsIdentifier, null, code, system, display, coding, codeableConcept, mySrd);
ourLog.info(result.getMessage()); ourLog.info(result.getMessage());
assertTrue(result.getMessage(), result.isResult()); 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.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; 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.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; 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.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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 { public class SearchParamExtractorR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParamExtractorR4Test.class);
private static FhirContext ourCtx = FhirContext.forR4(); private static FhirContext ourCtx = FhirContext.forR4();
private static IValidationSupport ourValidationSupport; private static IValidationSupport ourValidationSupport;
private ISearchParamRegistry mySearchParamRegistry;
@Test @Before
public void testParamWithOrInPath() { public void before() {
Observation obs = new Observation(); mySearchParamRegistry = new ISearchParamRegistry() {
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
ISearchParamRegistry searchParamRegistry = new ISearchParamRegistry() {
@Override @Override
public void forceRefresh() { public void forceRefresh() {
// nothing // nothing
@ -42,7 +48,7 @@ public class SearchParamExtractorR4Test {
@Override @Override
public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) { public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) {
throw new UnsupportedOperationException(); return getActiveSearchParams(theResourceName).get(theParamName);
} }
@Override @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); Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
assertEquals(1, tokens.size()); assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next(); ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -90,6 +103,50 @@ public class SearchParamExtractorR4Test {
assertEquals("CODE", token.getValue()); 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 @AfterClass
public static void afterClassClearContext() { public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();

View File

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

View File

@ -39,7 +39,7 @@ import java.nio.charset.StandardCharsets;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Matchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@TestPropertySource(properties = { @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.rest.api.EncodingEnum;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings("Duplicates")
public class PatientEverythingR4Test extends BaseResourceProviderR4Test { public class PatientEverythingR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientEverythingR4Test.class); 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) .onType(CodeSystem.class)
.named("lookup") .named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ACSN")) .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(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -245,7 +245,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named("lookup") .named("lookup")
.withParameter(Parameters.class, "code", new CodeType("M")) .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(); .execute();
//@formatter:on //@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.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.AuditEvent; import org.hl7.fhir.r4.model.*;
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.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode; 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.EncounterLocationComponent;
import org.hl7.fhir.r4.model.Encounter.EncounterStatus; import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; 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.Narrative.NarrativeStatus;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Observation.ObservationStatus; 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.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.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; 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.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; 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.TestUtil;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
@SuppressWarnings("Duplicates")
public class ResourceProviderR4Test extends BaseResourceProviderR4Test { public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class); 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(); IIdType optId = ourClient.create().resource(options).execute().getId();
Questionnaire q = new Questionnaire(); 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(); IIdType qId = ourClient.create().resource(q).execute().getId();
QuestionnaireResponse qa; 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)); location.setPeriod(new Period().setStart(new Date(), TemporalPrecisionEnum.SECOND).setEnd(new Date(), TemporalPrecisionEnum.SECOND));
IIdType e1id = ourClient.create().resource(e1).execute().getId(); IIdType e1id = ourClient.create().resource(e1).execute().getId();
//@formatter:off
Bundle res = ourClient.search() Bundle res = ourClient.search()
.forResource(Encounter.class) .forResource(Encounter.class)
.where(Encounter.IDENTIFIER.exactly().systemAndCode("urn:foo", "testDeepChainingE1")) .where(Encounter.IDENTIFIER.exactly().systemAndCode("urn:foo", "testDeepChainingE1"))
@ -755,7 +712,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.include(Location.INCLUDE_PARTOF.asRecursive()) .include(Location.INCLUDE_PARTOF.asRecursive())
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();
//@formatter:on
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res));
assertEquals(3, res.getEntry().size()); assertEquals(3, res.getEntry().size());
assertEquals(1, genResourcesOfType(res, Encounter.class).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 @Test
public void testDeleteConditionalMultiple() { public void testDeleteConditionalMultiple() {
String methodName = "testDeleteConditionalMultiple"; String methodName = "testDeleteConditionalMultiple";
@ -830,16 +827,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
String methodName = "testDeleteConditionalNoMatches"; String methodName = "testDeleteConditionalNoMatches";
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?identifier=" + methodName); HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?identifier=" + methodName);
CloseableHttpResponse resp = ourHttpClient.execute(delete); try (CloseableHttpResponse resp = ourHttpClient.execute(delete)) {
try {
ourLog.info(resp.toString()); ourLog.info(resp.toString());
String response = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8); String response = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response); ourLog.info(response);
assertEquals(200, resp.getStatusLine().getStatusCode()); assertEquals(200, resp.getStatusLine().getStatusCode());
assertThat(response, containsString( 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>")); "<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 @Test
public void testDeleteInvalidReference() throws IOException { public void testDeleteInvalidReference() throws IOException {
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient"); HttpDelete delete = new HttpDelete(ourServerBase + "/Patient");
CloseableHttpResponse response = ourHttpClient.execute(delete); try (CloseableHttpResponse response = ourHttpClient.execute(delete)) {
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseString); ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode()); assertEquals(400, response.getStatusLine().getStatusCode());
assertThat(responseString, containsString("Can not perform delete, no ID provided")); 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(); IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device(); Device dev = new Device();
dev.setModel(methodName); dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2); dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless(); IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
@ -1122,7 +1113,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getSubject().setReferenceElement(patientId); obs.getSubject().setReferenceElement(patientId);
obs.getDevice().setReferenceElement(devId); obs.getDevice().setReferenceElement(devId);
obs.getContext().setReferenceElement(encId); obs.getEncounter().setReferenceElement(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless(); IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
ourLog.info("IDs: EncU:" + encUId.getIdPart() + " Enc:" + encId.getIdPart() + " " + patientId.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(); IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device(); Device dev = new Device();
dev.setModel(methodName); dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2); dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless(); IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless();
@ -1185,7 +1176,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getSubject().setReferenceElement(patientId); obs.getSubject().setReferenceElement(patientId);
obs.getDevice().setReferenceElement(devId); obs.getDevice().setReferenceElement(devId);
obs.getContext().setReferenceElement(encId); obs.getEncounter().setReferenceElement(encId);
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless(); IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
Parameters output = ourClient.operation().onType(Encounter.class).named("everything").withNoParameters(Parameters.class).execute(); 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(); IIdType orgId2 = ourClient.create().resource(org2).execute().getId().toUnqualifiedVersionless();
Device dev = new Device(); Device dev = new Device();
dev.setModel(methodName); dev.setManufacturer(methodName);
dev.getOwner().setReferenceElement(orgId2); dev.getOwner().setReferenceElement(orgId2);
IIdType devId = ourClient.create().resource(dev).execute().getId().toUnqualifiedVersionless(); 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 * Per message from David Hay on Skype
*/ */
@Test @Test
@Ignore
public void testEverythingWithLargeSet() throws Exception { public void testEverythingWithLargeSet() throws Exception {
String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8); 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(id.withVersion("1").getValue(), history.getEntry().get(2).getResource().getId());
assertEquals(1, ((Patient) history.getEntry().get(2).getResource()).getName().size()); assertEquals(1, ((Patient) history.getEntry().get(2).getResource()).getName().size());
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(history));
try { try {
myBundleDao.validate(history, null, null, null, null, null, mySrd); myBundleDao.validate(history, null, null, null, null, null, mySrd);
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException e) {
@ -2537,7 +2531,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals("1", patientId.getVersionIdPart()); assertEquals("1", patientId.getVersionIdPart());
AuditEvent ae = new org.hl7.fhir.r4.model.AuditEvent(); 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(); IIdType aeId = ourClient.create().resource(ae).execute().getId();
assertEquals("1", aeId.getVersionIdPart()); assertEquals("1", aeId.getVersionIdPart());
@ -2546,8 +2540,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertFalse(patient.getManagingOrganization().getReferenceElement().hasVersionIdPart()); assertFalse(patient.getManagingOrganization().getReferenceElement().hasVersionIdPart());
ae = ourClient.read().resource(AuditEvent.class).withId(aeId).execute(); ae = ourClient.read().resource(AuditEvent.class).withId(aeId).execute();
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasIdPart()); assertTrue(ae.getEntityFirstRep().getWhat().getReferenceElement().hasIdPart());
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasVersionIdPart()); assertTrue(ae.getEntityFirstRep().getWhat().getReferenceElement().hasVersionIdPart());
} }
@ -3740,8 +3734,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
try { try {
ourClient ourClient
.search() .search()
.forResource(ImmunizationRecommendation.class) .forResource(Sequence.class)
.where(ImmunizationRecommendation.DOSE_NUMBER.withPrefix(ParamPrefixEnum.ENDS_BEFORE).number(100)) .where(Sequence.END.withPrefix(ParamPrefixEnum.ENDS_BEFORE).number(100))
.prettyPrint() .prettyPrint()
.returnBundle(Bundle.class) .returnBundle(Bundle.class)
.execute(); .execute();

View File

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

View File

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

View File

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

View File

@ -95,9 +95,9 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
.setPurpose("Monitor all admissions to Emergency") .setPurpose("Monitor all admissions to Emergency")
.setTrigger(new TriggerDefinition() .setTrigger(new TriggerDefinition()
.setType(TriggerDefinition.TriggerType.DATAADDED) .setType(TriggerDefinition.TriggerType.DATAADDED)
.setCondition(new TriggerDefinition.TriggerDefinitionConditionComponent() .setCondition(new Expression()
.setDescription("Encounter Location = emergency (active/completed encounters, current or previous)") .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()") .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"/> <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--> <!--CDA templateId: urn:hl7ii:2.16.840.1.113883.10.20.22.4.4:2015-08-01-->
</meta> </meta>
<clinicalStatus value="active"/>
<verificationStatus value="confirmed"/>
<category> <category>
<coding> <coding>
<system value="http://snomed.info/sct"/> <system value="http://snomed.info/sct"/>
@ -2116,8 +2114,6 @@
<profile value="http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition"/> <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--> <!--CDA templateId: urn:hl7ii:2.16.840.1.113883.10.20.22.4.4:2015-08-01-->
</meta> </meta>
<clinicalStatus value="active"/>
<verificationStatus value="confirmed"/>
<category> <category>
<coding> <coding>
<system value="http://hl7.org/fhir/v3/ActCode"/> <system value="http://hl7.org/fhir/v3/ActCode"/>

View File

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

View File

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

View File

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

View File

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

View File

@ -131,7 +131,7 @@
"binding": { "binding": {
"strength": "example", "strength": "example",
"description": "Codes for identifying types of resources not yet defined by FHIR", "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": [ "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.BaseTableColumnTypeTask;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableTask;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -126,6 +127,11 @@ public class JdbcUtils {
return BaseTableColumnTypeTask.ColumnTypeEnum.STRING.getDescriptor(length); return BaseTableColumnTypeTask.ColumnTypeEnum.STRING.getDescriptor(length);
case Types.BIGINT: case Types.BIGINT:
return BaseTableColumnTypeTask.ColumnTypeEnum.LONG.getDescriptor(null); 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: default:
throw new IllegalArgumentException("Don't know how to handle datatype: " + dataType); 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 * Constructor
*/ */
BaseTableColumnTypeTask() { 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.DERBY_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MARIADB_10_1, "bigint"); setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MARIADB_10_1, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MYSQL_5_7, "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"); Assert.isTrue(theColumnLength == null, "Must not supply a column length");
return "timestamp"; 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); public abstract String getDescriptor(Long theColumnLength);
} }

View File

@ -37,7 +37,22 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
* Constructor * Constructor
*/ */
public HapiFhirJpaMigrationTasks() { public HapiFhirJpaMigrationTasks() {
init340();
init350(); 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() { 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"); .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 java.sql.SQLException;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
public class AddColumnTest extends BaseTest { public class AddColumnTest extends BaseTest {
@ -26,6 +27,23 @@ public class AddColumnTest extends BaseTest {
assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "SOMETABLE"), containsInAnyOrder("PID", "TEXTCOL", "NEWCOL")); 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 @Test
public void testColumnAlreadyExists() throws SQLException { public void testColumnAlreadyExists() throws SQLException {
executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), newcol bigint)"); 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.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
retVal.setFetchSizeDefaultMaximum(10000); retVal.setFetchSizeDefaultMaximum(10000);
retVal.setReindexThreadCount(1);
retVal.setExpungeEnabled(true);
return retVal; return retVal;
} }

View File

@ -59,6 +59,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO); retVal.setCountSearchResultsUpTo(TestR4Config.COUNT_SEARCH_RESULTS_UP_TO);
retVal.setFetchSizeDefaultMaximum(10000); retVal.setFetchSizeDefaultMaximum(10000);
retVal.setExpungeEnabled(true);
return retVal; 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.BaseJpaSystemProvider;
import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; 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.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
@ -35,6 +36,9 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
return new RuleBuilder() return new RuleBuilder()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen() .deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen()
.deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).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() .allowAll()
.build(); .build();
} }

View File

@ -644,9 +644,11 @@ public class RestfulServerUtils {
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) { if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
if (fullId != null && fullId.hasVersionIdPart()) { 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())) { } 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); return response.sendWriterResponse(theStatusCode, contentType, charset, writer);
} }
public static String createEtag(String theVersionId) {
return "W/\"" + theVersionId + '"';
}
public static Integer tryToExtractNamedParameter(RequestDetails theRequest, String theParamName) { public static Integer tryToExtractNamedParameter(RequestDetails theRequest, String theParamName) {
String[] retVal = theRequest.getParameters().get(theParamName); String[] retVal = theRequest.getParameters().get(theParamName);
if (retVal == null) { 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;
import ca.uhn.fhir.util.BundleUtil.BundleEntryParts; import ca.uhn.fhir.util.BundleUtil.BundleEntryParts;
import ca.uhn.fhir.util.FhirTerser; 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.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IAnyResource; 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.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import javax.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -376,7 +375,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
String compartmentOwnerResourceType = next.getResourceType(); String compartmentOwnerResourceType = next.getResourceType();
if (!StringUtils.equals(appliesToResourceType, compartmentOwnerResourceType)) { if (!StringUtils.equals(appliesToResourceType, compartmentOwnerResourceType)) {
List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(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 * 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(HasParam.class, RestSearchParameterTypeEnum.HAS);
ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS); ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS);
ourParamTypes.put(HasAndListParam.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(); private List<Class<? extends IQueryParameterType>> myCompositeTypes = Collections.emptyList();

View File

@ -802,7 +802,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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("text")));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
@ -836,7 +836,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); assertThat(encoded, not(containsString("maritalStatus")));
@ -856,7 +856,7 @@ public class JsonParserDstu2_1Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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 + "\"")); "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));

View File

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

View File

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

View File

@ -808,7 +808,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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("text")));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
@ -918,7 +918,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); assertThat(encoded, not(containsString("maritalStatus")));
@ -940,7 +940,7 @@ public class JsonParserDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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 + "\",")); "\"code\": \"" + Constants.TAG_SUBSETTED_CODE + "\","));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));

View File

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

View File

@ -195,7 +195,9 @@ public interface IWorkerContext {
* @throws FHIRException * @throws FHIRException
*/ */
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException; public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException;
StructureDefinition fetchTypeDefinition(String theCode);
public class ValidationResult { public class ValidationResult {
private ConceptDefinitionComponent definition; private ConceptDefinitionComponent definition;
private IssueSeverity severity; 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.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator; import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.util.*; import java.util.*;
@ -69,6 +70,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
vso = getExpander().expand(theSource, theProfile); vso = getExpander().expand(theSource, theProfile);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
throw e; throw e;
} catch (TerminologyServiceException e) {
throw new InvalidRequestException(e.getMessage(), e);
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
@ -89,6 +92,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
return myValidationSupport.expandValueSet(myCtx, theInc); return myValidationSupport.expandValueSet(myCtx, theInc);
} }
@Override
public StructureDefinition fetchTypeDefinition(String theCode) {
throw new UnsupportedOperationException();
}
@Override @Override
public CodeSystem fetchCodeSystem(String theSystem) { public CodeSystem fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) { 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 @Override

View File

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

View File

@ -1023,7 +1023,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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("text")));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
@ -1057,7 +1057,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); assertThat(encoded, not(containsString("maritalStatus")));
@ -1077,7 +1077,7 @@ public class JsonParserDstu3Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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 + "\"")); "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));

View File

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

View File

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

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef; 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.rest.api.Constants;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import net.sf.json.JSON; 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.Patient.ContactComponent;
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent; import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent; 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.IIdType;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.*; import org.junit.*;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader; import java.io.StringReader;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
@ -148,7 +144,7 @@ public class JsonParserHl7OrgDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("Patient")); 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("text")));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
@ -780,7 +776,7 @@ public class JsonParserHl7OrgDstu2Test {
assertThat(encoded, containsString("Patient")); assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", 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, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); assertThat(encoded, not(containsString("maritalStatus")));
@ -802,7 +798,7 @@ public class JsonParserHl7OrgDstu2Test {
assertThat(encoded, containsString("Patient")); assertThat(encoded, containsString("Patient"));
assertThat(encoded, stringContainsInOrder("\"tag\"", assertThat(encoded, stringContainsInOrder("\"tag\"",
"\"system\": \"foo\",", "\"code\": \"bar\"", "\"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, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
assertThat(encoded, not(containsString("maritalStatus"))); 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.Enumerations.AdministrativeGender;
import org.hl7.fhir.instance.model.Identifier.IdentifierUse; import org.hl7.fhir.instance.model.Identifier.IdentifierUse;
import org.hl7.fhir.instance.model.Narrative.NarrativeStatus; 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.Observation;
import org.hl7.fhir.instance.model.Organization; import org.hl7.fhir.instance.model.Organization;
import org.hl7.fhir.instance.model.Patient; 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.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; 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.instance.model.api.IPrimitiveType;
import org.junit.After; import org.junit.After;
import org.junit.Assert; 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.context.api.AddProfileTagEnum;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef; 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.MyPatientWithOneDeclaredAddressExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredExtension; import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredExtension;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
@ -1050,7 +1046,7 @@ public class XmlParserHl7OrgDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("<Patient")); 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>")); "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("text"))); assertThat(encoded, not(containsString("text")));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
@ -1169,7 +1165,7 @@ public class XmlParserHl7OrgDstu2Test {
ourLog.info(encoded); ourLog.info(encoded);
assertThat(encoded, containsString("<Patient")); 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>")); "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));
@ -1191,7 +1187,7 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(encoded, containsString("<Patient")); assertThat(encoded, containsString("<Patient"));
assertThat(encoded, stringContainsInOrder("<tag>", "<system value=\"foo\"/>", "<code value=\"bar\"/>", "</tag>")); 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>")); "<code value=\"" + Constants.TAG_SUBSETTED_CODE + "\"/>", "</tag>"));
assertThat(encoded, not(containsString("THE DIV"))); assertThat(encoded, not(containsString("THE DIV")));
assertThat(encoded, containsString("family")); assertThat(encoded, containsString("family"));

View File

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

View File

@ -1,6 +1,9 @@
package org.hl7.fhir.r4.conformance; package org.hl7.fhir.r4.conformance;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -8,6 +11,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.hl7.fhir.exceptions.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.context.IWorkerContext;
import org.hl7.fhir.r4.formats.IParser; import org.hl7.fhir.r4.formats.IParser;
import org.hl7.fhir.r4.model.Base; 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.ElementDefinitionBindingComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionConstraintComponent;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionMappingComponent; 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.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r4.model.Enumerations.BindingStrength; import org.hl7.fhir.r4.model.Enumerations.BindingStrength;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; 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.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4.utils.DefinitionNavigator; import org.hl7.fhir.r4.utils.DefinitionNavigator;
import org.hl7.fhir.r4.utils.ToolingExtensions; 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.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities; 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;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import com.google.gson.JsonObject;
/** /**
* A engine that generates difference analysis between two sets of structure * A engine that generates difference analysis between two sets of structure
* definitions, typically from 2 different implementation guides. * definitions, typically from 2 different implementation guides.
@ -398,8 +407,11 @@ public class ProfileComparer {
if (isExtension(left.path())) if (isExtension(left.path()))
return compareExtensions(outcome, path, superset, subset, left, right); return compareExtensions(outcome, path, superset, subset, left, right);
// return true; // return true;
else else {
ElementDefinitionSlicingComponent slicingL = left.current().getSlicing();
ElementDefinitionSlicingComponent slicingR = right.current().getSlicing();
throw new DefinitionException("Slicing is not handled yet"); throw new DefinitionException("Slicing is not handled yet");
}
// todo: name // todo: name
} }
@ -629,8 +641,8 @@ public class ProfileComparer {
return false; return false;
} }
} }
subBinding.setValueSet(new Reference().setReference("#"+addValueSet(cvs))); subBinding.setValueSet("#"+addValueSet(cvs));
superBinding.setValueSet(new Reference().setReference("#"+addValueSet(unite(superset, outcome, path, lvs, rvs)))); superBinding.setValueSet("#"+addValueSet(unite(superset, outcome, path, lvs, rvs)));
} }
} }
return false; return false;
@ -649,11 +661,11 @@ public class ProfileComparer {
ValueSet lvs = resolveVS(outcome.left, left.getValueSet()); ValueSet lvs = resolveVS(outcome.left, left.getValueSet());
ValueSet rvs = resolveVS(outcome.left, right.getValueSet()); ValueSet rvs = resolveVS(outcome.left, right.getValueSet());
if (lvs != null && rvs != null) 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) else if (lvs != null)
union.setValueSet(new Reference().setReference("#"+addValueSet(lvs))); union.setValueSet("#"+addValueSet(lvs));
else if (rvs != null) else if (rvs != null)
union.setValueSet(new Reference().setReference("#"+addValueSet(rvs))); union.setValueSet("#"+addValueSet(rvs));
} }
return union; return union;
} }
@ -710,17 +722,10 @@ public class ProfileComparer {
return false; return false;
} }
private ValueSet resolveVS(StructureDefinition ctxtLeft, Type vsRef) { private ValueSet resolveVS(StructureDefinition ctxtLeft, String vsRef) {
if (vsRef == null) if (vsRef == null)
return null; return null;
if (vsRef instanceof UriType) return context.fetchResource(ValueSet.class, vsRef);
return null;
else {
Reference ref = (Reference) vsRef;
if (!ref.hasReference())
return null;
return context.fetchResource(ValueSet.class, ref.getReference());
}
} }
private ValueSet intersectByDefinition(ValueSet lvs, ValueSet rvs) { private ValueSet intersectByDefinition(ValueSet lvs, ValueSet rvs) {
@ -1166,6 +1171,100 @@ public class ProfileComparer {
this.rightName = rightName; 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.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair; 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.context.IWorkerContext;
import org.hl7.fhir.r4.elementmodel.TurtleParser; import org.hl7.fhir.r4.elementmodel.TurtleParser;
import org.hl7.fhir.r4.model.DomainResource; 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.model.ValueSet;
import org.hl7.fhir.r4.terminologies.ValueSetExpander; import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.FHIRException;
import org.stringtemplate.v4.ST; import org.stringtemplate.v4.ST;
public class ShExGenerator { public class ShExGenerator {
@ -388,7 +388,7 @@ public class ShExGenerator {
for (String dt : new HashSet<String>(datatypes)) { for (String dt : new HashSet<String>(datatypes)) {
if (!emittedDatatypes.contains(dt)) { if (!emittedDatatypes.contains(dt)) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, StructureDefinition sd = context.fetchResource(StructureDefinition.class,
ProfileUtilities.sdNs(dt)); ProfileUtilities.sdNs(dt, null));
// TODO: Figure out why the line below doesn't work // TODO: Figure out why the line below doesn't work
// if (sd != null && !uniq_structures.contains(sd)) // if (sd != null && !uniq_structures.contains(sd))
if(sd != null && !uniq_structure_urls.contains(sd.getUrl())) if(sd != null && !uniq_structure_urls.contains(sd.getUrl()))
@ -748,23 +748,11 @@ public class ShExGenerator {
// TODO: find a utility that implements this // TODO: find a utility that implements this
private ValueSet resolveBindingReference(DomainResource ctxt, Type reference) { private ValueSet resolveBindingReference(DomainResource ctxt, String reference) {
if (reference instanceof UriType) { try {
return context.fetchResource(ValueSet.class, ((UriType) reference).getValue().toString()); return context.fetchResource(ValueSet.class, reference);
} } catch (Throwable e) {
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
return null; 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.Coding;
import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 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.MetadataResource;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureMap; import org.hl7.fhir.r4.model.StructureMap;
@ -194,8 +194,8 @@ public interface IWorkerContext {
// -- Terminology services ------------------------------------------------------ // -- Terminology services ------------------------------------------------------
public ExpansionProfile getExpansionProfile(); public Parameters getExpansionParameters();
public void setExpansionProfile(ExpansionProfile expProfile); public void setExpansionProfile(Parameters expParameters);
// these are the terminology services used internally by the tools // these are the terminology services used internally by the tools
/** /**
@ -256,7 +256,7 @@ public interface IWorkerContext {
* @return * @return
* @throws FHIRException * @throws FHIRException
*/ */
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException; public ValueSetExpansionOutcome expandVS(ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException;
public class ValidationResult { public class ValidationResult {
private ConceptDefinitionComponent definition; private ConceptDefinitionComponent definition;
@ -286,7 +286,7 @@ public interface IWorkerContext {
} }
public boolean isOk() { public boolean isOk() {
return definition != null; return severity == null || severity == IssueSeverity.INFORMATION || severity == IssueSeverity.WARNING;
} }
public String getDisplay() { public String getDisplay() {
@ -314,6 +314,16 @@ public interface IWorkerContext {
public TerminologyServiceErrorClass getErrorClass() { public TerminologyServiceErrorClass getErrorClass() {
return errorClass; 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 * @return
*/ */
public ValidationResult validateCode(String system, String code, String display, ValueSet vs); 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(Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs); public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
@ -391,10 +402,16 @@ public interface IWorkerContext {
} }
public void setLogger(ILoggingService logger); public void setLogger(ILoggingService logger);
public ILoggingService getLogger();
public boolean isNoTerminologyServer(); public boolean isNoTerminologyServer();
public TranslationServices translator(); public TranslationServices translator();
public List<StructureMap> listTransforms(); public List<StructureMap> listTransforms();
public StructureMap getTransform(String url); 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.CSFileInputStream;
import org.hl7.fhir.utilities.OIDUtils; import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.Utilities; 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;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
@ -92,12 +93,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
private String date; private String date;
private IValidatorFactory validatorFactory; private IValidatorFactory validatorFactory;
private UcumService ucumService; private UcumService ucumService;
private boolean ignoreProfileErrors;
public SimpleWorkerContext() { public SimpleWorkerContext() throws FileNotFoundException, IOException, FHIRException {
super(); super();
} }
public SimpleWorkerContext(SimpleWorkerContext other) { public SimpleWorkerContext(SimpleWorkerContext other) throws FileNotFoundException, IOException, FHIRException {
super(); super();
copy(other); copy(other);
} }
@ -130,6 +132,26 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return res; 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 { public static SimpleWorkerContext fromPack(String path, boolean allowDuplicates) throws FileNotFoundException, IOException, FHIRException {
SimpleWorkerContext res = new SimpleWorkerContext(); SimpleWorkerContext res = new SimpleWorkerContext();
res.setAllowLoadingDuplicates(allowDuplicates); 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 { private void loadFromFileJson(InputStream stream, String name, IContextResourceLoader loader) throws IOException, FHIRException {
Bundle f; Bundle f = null;
try { try {
if (loader != null) if (loader != null)
f = loader.loadBundle(stream, true); f = loader.loadBundle(stream, true);
else { else {
JsonParser json = new JsonParser(); 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) { } catch (FHIRFormatError e1) {
throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1); throw new org.hl7.fhir.exceptions.FHIRFormatError(e1.getMessage(), e1);
} }
for (BundleEntryComponent e : f.getEntry()) { if (f != null)
for (BundleEntryComponent e : f.getEntry()) {
if (e.getFullUrl() == null) { cacheResource(e.getResource());
logger.logDebugMessage(LogCategory.CONTEXT, "unidentified resource in " + name+" (no fullUrl)");
}
cacheResource(e.getResource());
} }
} }
private void loadFromPack(String path, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException { private void loadFromPack(String path, IContextResourceLoader loader) throws FileNotFoundException, IOException, FHIRException {
loadFromStream(new CSFileInputStream(path), loader); 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 { public void loadFromFile(String file, IContextResourceLoader loader) throws IOException, FHIRException {
loadDefinitionItem(file, new CSFileInputStream(file), loader); loadDefinitionItem(file, new CSFileInputStream(file), loader);
@ -423,6 +459,18 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
return result; 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 { public void loadFromFolder(String folder) throws FileNotFoundException, Exception {
for (String n : new File(folder).list()) { for (String n : new File(folder).list()) {
if (n.endsWith(".json")) if (n.endsWith(".json"))
@ -507,12 +555,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
List<ValidationMessage> msgs = new ArrayList<ValidationMessage>(); List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
List<String> errors = new ArrayList<String>(); List<String> errors = new ArrayList<String>();
ProfileUtilities pu = new ProfileUtilities(this, msgs, this); ProfileUtilities pu = new ProfileUtilities(this, msgs, this);
pu.setThrowException(false);
pu.sortDifferential(sd, p, p.getUrl(), errors); pu.sortDifferential(sd, p, p.getUrl(), errors);
for (String err : errors) for (String err : errors)
msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getUserString("path"), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR)); 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()); pu.generateSnapshot(sd, p, p.getUrl(), p.getName());
for (ValidationMessage msg : msgs) { 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()); throw new DefinitionException("Profile "+p.getName()+" ("+p.getUrl()+"). Error generating snapshot: "+msg.getMessage());
} }
if (!p.hasSnapshot()) if (!p.hasSnapshot())
@ -531,6 +580,14 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
this.ucumService = ucumService; 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.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.exceptions.FHIRException; 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.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer; import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@ -470,9 +472,30 @@ public class Element extends Base {
return this; 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) { public Element getNamedChild(String name) {
if (children == null) if (children == null)
return null; return null;
@ -660,7 +683,7 @@ public class Element extends Base {
private List<ElementDefinition> children; private List<ElementDefinition> children;
public ElementSortComparator(Element e, Property property) { public ElementSortComparator(Element e, Property property) {
String tn = e.getType(); 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()) if (sd != null && !sd.getAbstract())
children = sd.getSnapshot().getElement(); children = sd.getSnapshot().getElement();
else else

View File

@ -80,7 +80,7 @@ public class JsonParser extends ParserBase {
assert (map.containsKey(obj)); assert (map.containsKey(obj));
return parse(obj); return parse(obj);
} else { } 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)); // assert (map.containsKey(obj));
return parse(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); logError(line(res), col(res), npath, IssueType.INVALID, "Unable to find resourceType property", IssueSeverity.FATAL);
} else { } else {
String name = rt.getAsString(); 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) if (sd == null)
throw new FHIRFormatError("Contained resource does not appear to be a FHIR resource (unknown name '"+name+"')"); 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); 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")) else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt"))
json.value(new Integer(item.getValue())); json.value(new Integer(item.getValue()));
else if (Utilities.existsInList(type, "decimal")) 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 else
json.value(item.getValue()); json.value(item.getValue());
} }

View File

@ -42,7 +42,7 @@ public class ObjectConverter {
if (base == null) if (base == null)
return null; return null;
String tn = base.fhirType(); 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) if (sd == null)
throw new FHIRException("Unable to find definition for type "+tn); throw new FHIRException("Unable to find definition for type "+tn);
Element res = new Element(property.getName(), property); Element res = new Element(property.getName(), property);
@ -86,7 +86,7 @@ public class ObjectConverter {
ByteArrayOutputStream bo = new ByteArrayOutputStream(); ByteArrayOutputStream bo = new ByteArrayOutputStream();
try { try {
new JsonParser(context).compose(element, bo, OutputStyle.NORMAL, null); 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()); return new org.hl7.fhir.r4.formats.JsonParser().parse(bo.toByteArray());
} catch (IOException e) { } catch (IOException e) {
// won't happen // won't happen

View File

@ -3,13 +3,16 @@ package org.hl7.fhir.r4.elementmodel;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.hl7.fhir.r4.context.IWorkerContext; import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.formats.FormatUtilities; import org.hl7.fhir.r4.formats.FormatUtilities;
import org.hl7.fhir.r4.formats.IParser.OutputStyle; import org.hl7.fhir.r4.formats.IParser.OutputStyle;
import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
@ -33,7 +36,7 @@ public abstract class ParserBase {
public boolean isPrimitive(String code) { 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"); 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; // return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
} }
@ -41,7 +44,8 @@ public abstract class ParserBase {
protected ValidationPolicy policy; protected ValidationPolicy policy;
protected List<ValidationMessage> errors; protected List<ValidationMessage> errors;
protected ILinkResolver linkResolver; protected ILinkResolver linkResolver;
protected boolean showDecorations;
public ParserBase(IWorkerContext context) { public ParserBase(IWorkerContext context) {
super(); super();
this.context = context; this.context = context;
@ -77,7 +81,7 @@ public abstract class ParserBase {
return null; return null;
} }
for (StructureDefinition sd : context.allStructures()) { 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")) if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
return sd; return sd;
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 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()) { for (StructureDefinition sd : context.allStructures()) {
if (name.equals(sd.getIdElement().getIdPart())) { if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
return sd; return sd;
} }
} }
@ -118,7 +122,13 @@ public abstract class ParserBase {
return this; 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.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.TypeDetails; import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.utils.ToolingExtensions; import org.hl7.fhir.r4.utils.ToolingExtensions;
import org.hl7.fhir.r4.utils.TypesUtilities;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
@ -88,7 +89,10 @@ public class Property {
} else } else
throw new Error("logic error, gettype when types > 1, name mismatch for "+elementName+" on at "+ed.getPath()); 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) { } 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 } else
return ed.getType().get(0).getCode(); return ed.getType().get(0).getCode();
} }
@ -135,8 +139,10 @@ public class Property {
* @param E.g. "integer" * @param E.g. "integer"
*/ */
public boolean isPrimitive(String code) { public boolean isPrimitive(String code) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code); return TypesUtilities.isPrimitive(code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; // 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) { private String lowFirst(String t) {
@ -191,7 +197,7 @@ public class Property {
return false; return false;
StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name)); StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name));
if (sd == null) 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) if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE)
return true; return true;
if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL) if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL)
@ -274,7 +280,7 @@ public class Property {
assert aType.getProfile().size() == 1; assert aType.getProfile().size() == 1;
url = aType.getProfile().get(0).getValue(); url = aType.getProfile().get(0).getValue();
} else { } else {
url = ProfileUtilities.sdNs(t); url = ProfileUtilities.sdNs(t, context.getOverrideVersionNs());
} }
break; break;
} }

View File

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

View File

@ -26,6 +26,7 @@ public interface JsonCreator {
void value(Boolean value) throws IOException; void value(Boolean value) throws IOException;
void value(BigDecimal 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; void value(Integer value) throws IOException;

View File

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