diff --git a/examples/src/main/java/example/ValidatorExamples.java b/examples/src/main/java/example/ValidatorExamples.java index db6d17e5d5d..49e368e410f 100644 --- a/examples/src/main/java/example/ValidatorExamples.java +++ b/examples/src/main/java/example/ValidatorExamples.java @@ -8,6 +8,11 @@ import javax.servlet.ServletException; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; +import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport; +import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator; +import org.hl7.fhir.instance.hapi.validation.IValidationSupport; +import org.hl7.fhir.instance.hapi.validation.IValidationSupport.CodeValidationResult; +import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain; import org.hl7.fhir.instance.model.ValueSet; import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent; @@ -23,11 +28,7 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.validation.DefaultProfileValidationSupport; -import ca.uhn.fhir.validation.ValidationSupportChain; -import ca.uhn.fhir.validation.FhirInstanceValidator; import ca.uhn.fhir.validation.FhirValidator; -import ca.uhn.fhir.validation.IValidationSupport; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java index 679438e35db..4129f6498d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java @@ -75,7 +75,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding myResourceName = theConetxt.getResourceDefinition(myType).getName(); - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); myVersionIdParamIndex = MethodUtil.findVersionIdParameterIndex(theMethod); myTagListParamIndex = MethodUtil.findTagListParameterIndex(theMethod); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java index 7c439070619..4b7d2be1091 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java @@ -75,7 +75,7 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu } myResourceName = theContext.getResourceDefinition(myResourceType).getName(); - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); if (myIdParamIndex != null) { myIdParamType = (Class) theMethod.getParameterTypes()[myIdParamIndex]; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java index 71d8678bcee..2ea31baeed2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java @@ -66,7 +66,7 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding { } } - myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); if (myIdParameterIndex == null) { throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation"); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchMethodBinding.java index 8b4296fbf41..a64b0ba03f3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchMethodBinding.java @@ -59,7 +59,7 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi myParamNames.add(next.getName()); } - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/GetTagsMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/GetTagsMethodBinding.java index 47d44af4794..6eb4e6e48df 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/GetTagsMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/GetTagsMethodBinding.java @@ -67,7 +67,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding { myResourceName = theConetxt.getResourceDefinition(myType).getName(); } - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); myVersionIdParamIndex = MethodUtil.findVersionIdParameterIndex(theMethod); if (myIdParamIndex != null && myType.equals(IResource.class)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java index fb83132e0d3..509bdcd91b2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java @@ -55,7 +55,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding { public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) { super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider); - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); History historyAnnotation = theMethod.getAnnotation(History.class); Class type = historyAnnotation.type(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index 29de514c8bd..1aa2bc303bb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -322,8 +322,20 @@ public class MethodUtil { return MethodUtil.findParamAnnotationIndex(theMethod, ConditionalUrlParam.class); } - public static Integer findIdParameterIndex(Method theMethod) { - return MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class); + public static Integer findIdParameterIndex(Method theMethod, FhirContext theContext) { + Integer index = MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class); + if (index != null) { + Class paramType = theMethod.getParameterTypes()[index]; + if (IIdType.class.equals(paramType)) { + return index; + } + boolean isRi = theContext.getVersion().getVersion().isRi(); + boolean usesHapiId = IdDt.class.equals(paramType); + if (isRi == usesHapiId) { + throw new ConfigurationException("Method uses the wrong Id datatype (IdDt / IdType) for the given context FHIR version: " + theMethod.toString()); + } + } + return index; } public static Integer findParamAnnotationIndex(Method theMethod, Class toFind) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java index 6365f7f7637..1cbd499c6aa 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java @@ -35,6 +35,7 @@ import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.context.ConfigurationException; @@ -42,7 +43,6 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -76,7 +76,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { super(theReturnResourceType, theMethod, theContext, theProvider); myIdempotent = theIdempotent; - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); if (myIdParamIndex != null) { for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) { if (next instanceof IdParam) { @@ -230,7 +230,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { String id = null; if (myIdParamIndex != null) { - IdDt idDt = (IdDt) theArgs[myIdParamIndex]; + IIdType idDt = (IIdType) theArgs[myIdParamIndex]; id = idDt.getValue(); } IBaseParameters parameters = (IBaseParameters) getContext().getResourceDefinition("Parameters").newInstance(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java index ae5cad5595e..8df59cd8b7e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java @@ -72,7 +72,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem Validate.notNull(theMethod, "Method must not be null"); - Integer idIndex = MethodUtil.findIdParameterIndex(theMethod); + Integer idIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); Integer versionIdIndex = MethodUtil.findVersionIdParameterIndex(theMethod); Class[] parameterTypes = theMethod.getParameterTypes(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java index 5e7b14ada08..203aa0ad5a6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java @@ -65,7 +65,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { Search search = theMethod.getAnnotation(Search.class); this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null); this.myCompartmentName = StringUtils.defaultIfBlank(search.compartmentName(), null); - this.myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + this.myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); this.myAllowUnknownParams = search.allowUnknownParams(); Description desc = theMethod.getAnnotation(Description.class); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java index 1e8578cfef8..669305759e8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java @@ -45,7 +45,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) { super(theMethod, theContext, Update.class, theProvider); - myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java index 0949c6ea175..b2dd926c683 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java @@ -40,7 +40,7 @@ public class ValidateMethodBindingDstu1 extends BaseOutcomeReturningMethodBindin public ValidateMethodBindingDstu1(Method theMethod, FhirContext theContext, Object theProvider) { super(theMethod, theContext, Validate.class, theProvider); - myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod); + myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); } @Override diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupport.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupport.java index b72ea2b7eda..6cffd283c1d 100644 --- a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupport.java +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupport.java @@ -1,15 +1,15 @@ package ca.uhn.fhir.cli; -import org.hl7.fhir.instance.model.ValueSet; -import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent; +import org.hl7.fhir.dstu21.hapi.validation.IValidationSupport; +import org.hl7.fhir.dstu21.model.ValueSet; +import org.hl7.fhir.dstu21.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.validation.IValidationSupport; public class LoadingValidationSupport implements IValidationSupport { diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java index 0e9280d0216..cda71350802 100644 --- a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java @@ -4,15 +4,13 @@ import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.leftPad; -import static org.fusesource.jansi.Ansi.*; +import static org.fusesource.jansi.Ansi.ansi; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; @@ -21,24 +19,20 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.text.WordUtils; -import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; +import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport; +import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator; +import org.hl7.fhir.dstu21.hapi.validation.ValidationSupportChain; +import org.hl7.fhir.dstu21.model.StructureDefinition; import com.phloc.commons.io.file.FileUtils; -import com.sun.tools.corba.se.idl.ParameterEntry; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.dstu2.resource.StructureDefinition; import ca.uhn.fhir.rest.method.MethodUtil; -import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.EncodingEnum; -import ca.uhn.fhir.validation.DefaultProfileValidationSupport; -import ca.uhn.fhir.validation.FhirInstanceValidator; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; -import ca.uhn.fhir.validation.ValidationSupportChain; -import net.sf.saxon.expr.instruct.LocalParam; -import net.sf.saxon.om.Chain; public class ValidateCommand extends BaseCommand { @@ -112,7 +106,7 @@ public class ValidateCommand extends BaseCommand { throw new ParseException("Failed to load file '" + localProfile + "' - Error: " + e.toString()); } - org.hl7.fhir.instance.model.StructureDefinition sd = (org.hl7.fhir.instance.model.StructureDefinition) MethodUtil.detectEncodingNoDefault(input).newParser(FhirContext.forDstu2Hl7Org()).parseResource(input); + StructureDefinition sd = (StructureDefinition) MethodUtil.detectEncodingNoDefault(input).newParser(getFhirCtx()).parseResource(input); instanceValidator.setStructureDefintion(sd); } if (theCommandLine.hasOption("r")) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu21.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu21.java index 166383302d4..7432e5bf210 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu21.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu21.java @@ -23,11 +23,10 @@ package ca.uhn.fhir.jpa.dao; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import java.io.FileNotFoundException; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -36,7 +35,6 @@ import javax.annotation.PostConstruct; import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu21.hapi.validation.HapiWorkerContext; -import org.hl7.fhir.dstu21.hapi.validation.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu21.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu21.model.CodeableConcept; import org.hl7.fhir.dstu21.model.Coding; @@ -48,19 +46,16 @@ import org.hl7.fhir.dstu21.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.dstu21.terminologies.ValueSetExpander; -import org.hl7.fhir.dstu21.terminologies.ValueSetExpander.ETooCostly; import org.hl7.fhir.dstu21.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; -import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; public class FhirResourceDaoValueSetDstu21 extends FhirResourceDaoDstu21 implements IFhirResourceDaoValueSet { @@ -81,31 +76,23 @@ public class FhirResourceDaoValueSetDstu21 extends FhirResourceDaoDstu21 containsIter = expansion.getContains().iterator(); containsIter.hasNext();) { + ValueSetExpansionContainsComponent nextContains = containsIter.next(); + if (!nextContains.getDisplay().toLowerCase().contains(theFilter.toLowerCase())) { + containsIter.remove(); + } } } - BaseHasResource sourceEntity = readEntity(theId); - if (sourceEntity == null) { - throw new ResourceNotFoundException(theId); - } - ValueSet source = (ValueSet) toResource(sourceEntity, false); - return source; + + ValueSet retVal = new ValueSet(); + retVal.setExpansion(expansion); + return retVal; } @Override @@ -239,9 +226,9 @@ public class FhirResourceDaoValueSetDstu21 extends FhirResourceDaoDstu21 findValueSetIdsContainingSystemAndCode(String theCode, String theSystem) { - if (theSystem != null && theSystem.startsWith("http://hl7.org/fhir/ValueSet")) { - return Collections.singletonList((IIdType) new IdType(theSystem)); - } +// if (theSystem != null && theSystem.startsWith("http://hl7.org/fhir/ValueSet")) { +// return Collections.singletonList((IIdType) new IdType(theSystem)); +// } List valueSetIds; Set ids = searchForIds(ValueSet.SP_CODE, new TokenParam(theSystem, theCode)); @@ -320,48 +307,65 @@ public class FhirResourceDaoValueSetDstu21 extends FhirResourceDaoDstu21 valueSetIds = findValueSetIdsContainingSystemAndCode(code, system); -// for (IIdType nextId : valueSetIds) { -// ValueSet expansion = expand(nextId, null); -// List contains = expansion.getExpansion().getContains(); -// ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.LookupCodeResult result = lookup(contains, system, code); -// if (result != null) { -// return result; -// } -// } -// - - HapiWorkerContext ctx = new HapiWorkerContext(getContext(), myValidationSupport); - ValueSetExpander expander = ctx.getExpander(); - ValueSet source = new ValueSet(); - source.getCompose().addInclude().setSystem(system).addConcept().setCode(code); - - ValueSetExpansionOutcome expansion; - try { - expansion = expander.expand(source); - } catch (Exception e) { - throw new InternalErrorException(e); + // CodeValidationResult validateOutcome = myJpaValidationSupport.validateCode(getContext(), system, code, null); + // + // LookupCodeResult result = new LookupCodeResult(); + // result.setSearchedForCode(code); + // result.setSearchedForSystem(system); + // result.setFound(false); + // if (validateOutcome.isOk()) { + // result.setFound(true); + // result.setCodeIsAbstract(validateOutcome.asConceptDefinition().getAbstract()); + // result.setCodeDisplay(validateOutcome.asConceptDefinition().getDisplay()); + // } + // return result; + + if (myValidationSupport.isCodeSystemSupported(getContext(), system)) { + HapiWorkerContext ctx = new HapiWorkerContext(getContext(), myValidationSupport); + ValueSetExpander expander = ctx.getExpander(); + ValueSet source = new ValueSet(); + source.getCompose().addInclude().setSystem(system).addConcept().setCode(code); + + ValueSetExpansionOutcome expansion; + try { + expansion = expander.expand(source); + } catch (Exception e) { + throw new InternalErrorException(e); + } + List contains = expansion.getValueset().getExpansion().getContains(); + ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.LookupCodeResult result = lookup(contains, system, code); + if (result != null) { + return result; + } + + } else { + + /* + * If it's not a built-in code system, use ones from the database + */ + + List valueSetIds = findValueSetIdsContainingSystemAndCode(code, system); + for (IIdType nextId : valueSetIds) { + ValueSet expansion = read(nextId); + for (ConceptDefinitionComponent next : expansion.getCodeSystem().getConcept()) { + if (code.equals(next.getCode())) { + ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.LookupCodeResult retVal = new LookupCodeResult(); + retVal.setSearchedForCode(code); + retVal.setSearchedForSystem(system); + retVal.setFound(true); + if (next.getAbstractElement().getValue() != null) { + retVal.setCodeIsAbstract(next.getAbstractElement().booleanValue()); + } + retVal.setCodeDisplay(next.getDisplay()); + retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement + return retVal; + } + } + } + } - List contains = expansion.getValueset().getExpansion().getContains(); - ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.LookupCodeResult result = lookup(contains, system, code); - if (result != null) { - return result; - } - + + // We didn't find it.. LookupCodeResult retVal = new LookupCodeResult(); retVal.setFound(false); retVal.setSearchedForCode(code); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu21.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu21.java index 588acdb1a8b..2156e1b267c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu21.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu21.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.dao; +import org.hl7.fhir.dstu21.model.IdType; import org.hl7.fhir.dstu21.model.StructureDefinition; import org.hl7.fhir.dstu21.model.ValueSet; import org.hl7.fhir.dstu21.model.ValueSet.ConceptSetComponent; @@ -30,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.IBundleProvider; @@ -61,10 +63,20 @@ public class JpaValidationSupportDstu21 implements IJpaValidationSupportDstu21 { @Override public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + IdType id = new IdType(theUri); + boolean localReference = false; + if (id.hasBaseUrl() == false && id.hasIdPart() == true) { + localReference = true; + } + String resourceName = myDstu21Ctx.getResourceDefinition(theClass).getName(); IBundleProvider search; if ("ValueSet".equals(resourceName)) { - search = myValueSetDao.search(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri)); + if (localReference) { + search = myValueSetDao.search(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_RES_ID, new StringParam(theUri)); + } else { + search = myValueSetDao.search(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri)); + } } else if ("StructureDefinition".equals(resourceName)) { search = myStructureDefinitionDao.search(ca.uhn.fhir.model.dstu2.resource.StructureDefinition.SP_URL, new UriParam(theUri)); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java index 62d39d7cb01..a243d483a75 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java @@ -30,7 +30,6 @@ import org.springframework.beans.factory.annotation.Required; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.GetTags; import ca.uhn.fhir.rest.annotation.History; import ca.uhn.fhir.rest.annotation.IdParam; @@ -83,7 +82,7 @@ public abstract class BaseJpaResourceProvider extends B } @GetTags - public TagList getTagsForResourceInstance(HttpServletRequest theRequest, @IdParam IdDt theResourceId) { + public TagList getTagsForResourceInstance(HttpServletRequest theRequest, @IdParam IIdType theResourceId) { startRequest(theRequest); try { return myDao.getTags(theResourceId); @@ -103,7 +102,7 @@ public abstract class BaseJpaResourceProvider extends B } @Read(version = true) - public T read(HttpServletRequest theRequest, @IdParam IdDt theId) { + public T read(HttpServletRequest theRequest, @IdParam IIdType theId) { startRequest(theRequest); try { return myDao.read(theId); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoValueSetDstu21Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoValueSetDstu21Test.java index 42c92aa3134..3ea2e4b94f5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoValueSetDstu21Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoValueSetDstu21Test.java @@ -35,7 +35,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { @Before @Transactional public void before02() throws IOException { - ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml"); + ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.1.xml"); upload.setId(""); myExtensionalVsId = myValueSetDao.create(upload).getId().toUnqualifiedVersionless(); } @@ -45,7 +45,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = null; IdType id = null; CodeType code = new CodeType("8450-9-XXX"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; @@ -58,7 +58,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = null; IdType id = null; CodeType code = new CodeType("8450-9"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; @@ -72,7 +72,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); IdType id = null; CodeType code = new CodeType("11378-7"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; @@ -86,7 +86,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); IdType id = null; CodeType code = new CodeType("11378-7"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = new StringType("Systolic blood pressure at First encounterXXXX"); Coding coding = null; CodeableConcept codeableConcept = null; @@ -100,7 +100,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); IdType id = null; CodeType code = new CodeType("11378-7"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = new StringType("Systolic blood pressure at First encounter"); Coding coding = null; CodeableConcept codeableConcept = null; @@ -118,7 +118,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { StringType display = null; Coding coding = null; CodeableConcept codeableConcept = new CodeableConcept(); - codeableConcept.addCoding().setSystem("http://loinc.org").setCode("11378-7"); + codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7"); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); @@ -129,7 +129,7 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { UriType valueSetIdentifier = null; IIdType id = myExtensionalVsId; CodeType code = new CodeType("11378-7"); - UriType system = new UriType("http://loinc.org"); + UriType system = new UriType("http://acme.org"); StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; @@ -150,15 +150,15 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { stringContainsInOrder("", "", "", - "", - "", - "", - "", - "", - "", + "", "", "", "", + "", + "", + "", + "", + "", "" )); //@formatter:on @@ -176,18 +176,6 @@ public class FhirResourceDaoValueSetDstu21Test extends BaseJpaDstu21Test { "")); //@formatter:on - /* - * Filter with code - */ - - expanded = myValueSetDao.expand(myExtensionalVsId, ("11378")); - resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); - ourLog.info(resp); - //@formatter:off - assertThat(resp, stringContainsInOrder( - "", - "")); - //@formatter:on } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21Test.java index 2451da6773f..3ee3702ea37 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21Test.java @@ -2299,7 +2299,7 @@ public class ResourceProviderDstu21Test extends BaseResourceProviderDstu21Test { @Test public void testValueSetExpandOperation() throws IOException { - ValueSet upload = myFhirCtx.newXmlParser().parseResource(ValueSet.class, new InputStreamReader(ResourceProviderDstu21Test.class.getResourceAsStream("/extensional-case-2.xml"))); + ValueSet upload = myFhirCtx.newXmlParser().parseResource(ValueSet.class, new InputStreamReader(ResourceProviderDstu21Test.class.getResourceAsStream("/extensional-case-2.1.xml"))); IIdType vsid = ourClient.create().resource(upload).execute().getId().toUnqualifiedVersionless(); HttpGet get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand"); @@ -2313,15 +2313,15 @@ public class ResourceProviderDstu21Test extends BaseResourceProviderDstu21Test { stringContainsInOrder("", "", "", - "", - "", - "", - "", - "", - "", + "", "", "", "", + "", + "", + "", + "", + "", "" )); //@formatter:on @@ -2350,27 +2350,6 @@ public class ResourceProviderDstu21Test extends BaseResourceProviderDstu21Test { response.close(); } - /* - * Filter with code - */ - - get = new HttpGet(ourServerBase + "/ValueSet/" + vsid.getIdPart() + "/$expand?filter=11378"); - response = ourHttpClient.execute(get); - try { - String resp = IOUtils.toString(response.getEntity().getContent()); - ourLog.info(resp); - assertEquals(200, response.getStatusLine().getStatusCode()); - //@formatter:off - assertThat(resp, stringContainsInOrder( - "", - "" - )); - //@formatter:on - } finally { - IOUtils.closeQuietly(response.getEntity().getContent()); - response.close(); - } - } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21ValueSetTest.java index 1ce5c798bf9..188bcdf12f9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/ResourceProviderDstu21ValueSetTest.java @@ -22,179 +22,21 @@ import org.junit.Test; import org.springframework.transaction.annotation.Transactional; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; public class ResourceProviderDstu21ValueSetTest extends BaseResourceProviderDstu21Test { - private IIdType myExtensionalVsId; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu21ValueSetTest.class); + private IIdType myExtensionalVsId; @Before @Transactional public void before02() throws IOException { - ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml"); + ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.1.xml"); upload.setId(""); myExtensionalVsId = myValueSetDao.create(upload).getId().toUnqualifiedVersionless(); } - @Test - public void testValidateCodeOperationByCodeAndSystemInstance() { - //@formatter:off - Parameters respParam = ourClient - .operation() - .onInstance(myExtensionalVsId) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8495-4")) - .andParameter("system", new UriType("http://loinc.org")) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); - } - - @Test - public void testValidateCodeOperationByCodeAndSystemType() { - //@formatter:off - Parameters respParam = ourClient - .operation() - .onType(ValueSet.class) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://loinc.org")) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); - } - - @Test - public void testLookupOperationByCodeAndSystem() { - //@formatter:off - Parameters respParam = ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://loinc.org")) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); - } - - @Test -// @Ignore - public void testLookupOperationForBuiltInCode() { - //@formatter:off - Parameters respParam = ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "code", new CodeType("M")) - .andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus")) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Married", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue()); - } - - @Test - public void testLookupOperationByCoding() { - //@formatter:off - Parameters respParam = ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "coding", new Coding().setSystem("http://loinc.org").setCode("8450-9")) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); - } - - @Test - public void testLookupOperationByInvalidCombination() { - //@formatter:off - try { - ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "coding", new Coding().setSystem("http://loinc.org").setCode("8450-9")) - .andParameter("code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://loinc.org")) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage()); - } - //@formatter:on - } - - @Test - public void testLookupOperationByInvalidCombination2() { - //@formatter:off - try { - ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "coding", new Coding().setSystem("http://loinc.org").setCode("8450-9")) - .andParameter("system", new UriType("http://loinc.org")) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage()); - } - //@formatter:on - } - - @Test - public void testLookupOperationByInvalidCombination3() { - //@formatter:off - try { - ourClient - .operation() - .onType(ValueSet.class) - .named("lookup") - .withParameter(Parameters.class, "coding", new Coding().setSystem("http://loinc.org").setCode(null)) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate", e.getMessage()); - } - //@formatter:on - } - @Test public void testExpandById() throws IOException { //@formatter:off @@ -214,15 +56,15 @@ public class ResourceProviderDstu21ValueSetTest extends BaseResourceProviderDstu stringContainsInOrder("", "", "", - "", - "", - "", - "", - "", - "", + "", "", "", "", + "", + "", + "", + "", + "", "" )); //@formatter:on @@ -250,28 +92,8 @@ public class ResourceProviderDstu21ValueSetTest extends BaseResourceProviderDstu "")); //@formatter:on - /* - * Filter with code - */ - - //@formatter:off - respParam = ourClient - .operation() - .onInstance(myExtensionalVsId) - .named("expand") - .withParameter(Parameters.class, "filter", new StringType("11378")) - .execute(); - expanded = (ValueSet) respParam.getParameter().get(0).getResource(); - //@formatter:on - resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); - ourLog.info(resp); - //@formatter:off - assertThat(resp, stringContainsInOrder( - "", - "")); - //@formatter:on } - + @Test public void testExpandByIdentifier() { //@formatter:off @@ -371,5 +193,223 @@ public class ResourceProviderDstu21ValueSetTest extends BaseResourceProviderDstu //@formatter:on } + + @Test + public void testLookupOperationByCodeAndSystemBuiltInCode() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://hl7.org/fhir/v2/0203")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals("Accession ID", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSNAAAAAA")) + .andParameter("system", new UriType("http://hl7.org/fhir/v2/0203")) + .execute(); + fail(); + } catch (ResourceNotFoundException e) { + // good + } + //@formatter:on + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedCode() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedNonExistantCode() { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9AAAAA")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + fail(); + } catch (ResourceNotFoundException e) { + // good + } + //@formatter:on + } + + @Test + public void testLookupOperationByCoding() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + } + + @Test + public void testLookupOperationByInvalidCombination() { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .andParameter("code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage()); + } + //@formatter:on + } + + @Test + public void testLookupOperationByInvalidCombination2() { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage()); + } + //@formatter:on + } + + @Test + public void testLookupOperationByInvalidCombination3() { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode(null)) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate", e.getMessage()); + } + //@formatter:on + } + + @Test +// @Ignore + public void testLookupOperationForBuiltInCode() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("M")) + .andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals("Married", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstance() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationByCodeAndSystemType() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/SystemProviderDstu21Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/SystemProviderDstu21Test.java index 2a339cea689..9ff205a4c44 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/SystemProviderDstu21Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu21/SystemProviderDstu21Test.java @@ -95,7 +95,7 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test { servletHolder.setServlet(restServer); proxyHandler.addServlet(servletHolder, "/fhir/context/*"); - ourCtx = FhirContext.forDstu2(); + ourCtx = FhirContext.forDstu2_1(); restServer.setFhirContext(ourCtx); ourServer.setHandler(proxyHandler); @@ -213,9 +213,9 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test { Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output); assertEquals(2, parameters.getParameter().size()); assertEquals("keyword", parameters.getParameter().get(0).getPart().get(0).getName()); - assertEquals(new StringType("ZXCVBNM"), parameters.getParameter().get(0).getPart().get(0).getValue()); + assertEquals(("ZXCVBNM"), ((StringType)parameters.getParameter().get(0).getPart().get(0).getValue()).getValueAsString()); assertEquals("score", parameters.getParameter().get(0).getPart().get(1).getName()); - assertEquals(new DecimalType("1.0"), parameters.getParameter().get(0).getPart().get(1).getValue()); + assertEquals(("1.0"), ((DecimalType)parameters.getParameter().get(0).getPart().get(1).getValue()).getValueAsString()); } finally { http.close(); @@ -356,7 +356,7 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test { } catch (InvalidRequestException e) { OperationOutcome oo = (OperationOutcome) e.getOperationOutcome(); assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics()); - assertEquals("processing", oo.getIssue().get(0).getCode()); + assertEquals("processing", oo.getIssue().get(0).getCode().toCode()); } } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.1.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.1.xml new file mode 100644 index 00000000000..07013a125ff --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-2.1.xml @@ -0,0 +1,121 @@ + + + + +
A selection of codes from http://acme.org
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 7ccaeff93b9..ad5f0af60c0 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -182,7 +182,7 @@ maven-surefire-plugin 2.19 - false + random -Dfile.encoding=UTF-8 false diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/rest/server/ServerProfileProvider.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/rest/server/ServerProfileProvider.java index f81dbcac52d..921c30d1500 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/rest/server/ServerProfileProvider.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/rest/server/ServerProfileProvider.java @@ -27,12 +27,12 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; +import org.hl7.fhir.dstu21.model.IdType; import org.hl7.fhir.dstu21.model.StructureDefinition; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; @@ -55,7 +55,7 @@ public class ServerProfileProvider implements IResourceProvider { } @Read() - public StructureDefinition getProfileById(HttpServletRequest theRequest, @IdParam IdDt theId) { + public StructureDefinition getProfileById(HttpServletRequest theRequest, @IdParam IdType theId) { RuntimeResourceDefinition retVal = myContext.getResourceDefinitionById(theId.getIdPart()); if (retVal==null) { return null; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/DefaultProfileValidationSupport.java index f2528f60e9d..f03e430b082 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/DefaultProfileValidationSupport.java @@ -6,7 +6,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.hl7.fhir.dstu21.model.Bundle; import org.hl7.fhir.dstu21.model.Bundle.BundleEntryComponent; @@ -14,6 +16,7 @@ import org.hl7.fhir.dstu21.model.IdType; import org.hl7.fhir.dstu21.model.OperationOutcome.IssueSeverity; import org.hl7.fhir.dstu21.model.ValueSet; import org.hl7.fhir.dstu21.model.ValueSet.ConceptDefinitionComponent; +import org.hl7.fhir.dstu21.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.dstu21.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -30,7 +33,21 @@ public class DefaultProfileValidationSupport implements IValidationSupport { @Override public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { - return null; + ValueSetExpansionComponent retVal = new ValueSetExpansionComponent(); + + Set wantCodes = new HashSet(); + for (ConceptReferenceComponent next : theInclude.getConcept()) { + wantCodes.add(next.getCode()); + } + + ValueSet system = fetchCodeSystem(theContext, theInclude.getSystem()); + for (ConceptDefinitionComponent next : system.getCodeSystem().getConcept()) { + if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) { + retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + } + } + + return retVal; } @Override @@ -98,7 +115,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport { } private void loadCodeSystems(FhirContext theContext, Map theCodeSystems, String theClasspath) { - ourLog.info("Loading code systems from file: {}", theClasspath); + ourLog.info("Loading code systems from classpath: {}", theClasspath); InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); if (valuesetText != null) { InputStreamReader reader; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/HapiWorkerContext.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/HapiWorkerContext.java index a49f5538a89..cb9aa22eef7 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/HapiWorkerContext.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/hapi/validation/HapiWorkerContext.java @@ -28,6 +28,7 @@ import org.hl7.fhir.dstu21.validation.IResourceValidator; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander, ValueSetExpanderFactory { private final FhirContext myCtx; @@ -169,7 +170,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander throw new InternalErrorException(e); } if (vso.getError() != null) { - return null; + throw new InvalidRequestException(vso.getError()); } else { return vso; } diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DateType.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DateType.java index 3c22336c19a..d60d34c645b 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DateType.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DateType.java @@ -119,7 +119,7 @@ public class DateType extends BaseDateTimeType { @Override public DateType copy() { - return new DateType(getValue()); + return new DateType(getValueAsString()); } public static InstantType today() { diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DocumentManifest.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DocumentManifest.java index 6d7975aaae8..a69b8c4b492 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DocumentManifest.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/DocumentManifest.java @@ -56,7 +56,7 @@ public class DocumentManifest extends DomainResource { /** * The list of references to document content, or Attachment that consist of the parts of this document manifest. Usually, these would be document references, but direct references to Media or Attachments are also allowed. */ - @Child(name = "p", type = {Attachment.class}, order=1, min=1, max=1, modifier=false, summary=true) + @Child(name = "p", type = {Attachment.class, ValueSet.class}, order=1, min=1, max=1, modifier=false, summary=true) @Description(shortDefinition="Contents of this set of documents", formalDefinition="The list of references to document content, or Attachment that consist of the parts of this document manifest. Usually, these would be document references, but direct references to Media or Attachments are also allowed." ) protected Type p; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/InstantType.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/InstantType.java index 684f7a36a7f..81fd1b0d6bf 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/InstantType.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/model/InstantType.java @@ -197,7 +197,7 @@ public class InstantType extends BaseDateTimeType { @Override public InstantType copy() { - return new InstantType(getValue()); + return new InstantType(getValueAsString()); } /** diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/utils/ProfileUtilities.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/utils/ProfileUtilities.java deleted file mode 100644 index edfe9bbaa08..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu21/utils/ProfileUtilities.java +++ /dev/null @@ -1,2138 +0,0 @@ -package org.hl7.fhir.dstu21.utils; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.hl7.fhir.dstu21.exceptions.DefinitionException; -import org.hl7.fhir.dstu21.exceptions.FHIRException; -import org.hl7.fhir.dstu21.formats.IParser; -import org.hl7.fhir.dstu21.model.Base; -import org.hl7.fhir.dstu21.model.BooleanType; -import org.hl7.fhir.dstu21.model.Coding; -import org.hl7.fhir.dstu21.model.Element; -import org.hl7.fhir.dstu21.model.ElementDefinition; -import org.hl7.fhir.dstu21.model.ElementDefinition.ElementDefinitionBindingComponent; -import org.hl7.fhir.dstu21.model.ElementDefinition.ElementDefinitionConstraintComponent; -import org.hl7.fhir.dstu21.model.ElementDefinition.ElementDefinitionMappingComponent; -import org.hl7.fhir.dstu21.model.ElementDefinition.ElementDefinitionSlicingComponent; -import org.hl7.fhir.dstu21.model.ElementDefinition.SlicingRules; -import org.hl7.fhir.dstu21.model.ElementDefinition.TypeRefComponent; -import org.hl7.fhir.dstu21.model.Enumerations.BindingStrength; -import org.hl7.fhir.dstu21.model.IntegerType; -import org.hl7.fhir.dstu21.model.OperationOutcome.IssueSeverity; -import org.hl7.fhir.dstu21.model.OperationOutcome.IssueType; -import org.hl7.fhir.dstu21.model.PrimitiveType; -import org.hl7.fhir.dstu21.model.Reference; -import org.hl7.fhir.dstu21.model.Resource; -import org.hl7.fhir.dstu21.model.StringType; -import org.hl7.fhir.dstu21.model.StructureDefinition; -import org.hl7.fhir.dstu21.model.StructureDefinition.StructureDefinitionDifferentialComponent; -import org.hl7.fhir.dstu21.model.StructureDefinition.StructureDefinitionSnapshotComponent; -import org.hl7.fhir.dstu21.model.Type; -import org.hl7.fhir.dstu21.model.UriType; -import org.hl7.fhir.dstu21.model.ValueSet; -import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionComponent; -import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.dstu21.terminologies.ValueSetExpander.ValueSetExpansionOutcome; -import org.hl7.fhir.dstu21.utils.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution; -import org.hl7.fhir.dstu21.validation.ValidationMessage; -import org.hl7.fhir.dstu21.validation.ValidationMessage.Source; -import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.Utilities; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; -import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; -import org.hl7.fhir.utilities.xhtml.XhtmlNode; -import org.hl7.fhir.utilities.xml.SchematronWriter; -import org.hl7.fhir.utilities.xml.SchematronWriter.Rule; -import org.hl7.fhir.utilities.xml.SchematronWriter.SchematronType; -import org.hl7.fhir.utilities.xml.SchematronWriter.Section; - -/** - * This class provides a set of utility operations for working with Profiles. - * Key functionality: - * * getChildMap --? - * * getChildList - * * generateSnapshot: Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile - * * generateExtensionsTable: generate the HTML for a hierarchical table presentation of the extensions - * * generateTable: generate the HTML for a hierarchical table presentation of a structure - * * summarise: describe the contents of a profile - * @author Grahame - * - */ -public class ProfileUtilities { - - public class ExtensionContext { - - private ElementDefinition element; - private StructureDefinition defn; - - public ExtensionContext(StructureDefinition ext, ElementDefinition ed) { - this.defn = ext; - this.element = ed; - } - - public ElementDefinition getElement() { - return element; - } - - public StructureDefinition getDefn() { - return defn; - } - - public String getUrl() { - if (element == defn.getSnapshot().getElement().get(0)) - return defn.getUrl(); - else - return element.getName(); - } - - public ElementDefinition getExtensionValueDefinition() { - int i = defn.getSnapshot().getElement().indexOf(element)+1; - while (i < defn.getSnapshot().getElement().size()) { - ElementDefinition ed = defn.getSnapshot().getElement().get(i); - if (ed.getPath().equals(element.getPath())) - return null; - if (ed.getPath().startsWith(element.getPath()+".value")) - return ed; - i++; - } - return null; - } - - } - - - - - private final boolean ADD_REFERENCE_TO_TABLE = true; - - - private static final String ROW_COLOR_ERROR = "#ffcccc"; - private static final String ROW_COLOR_FATAL = "#ff9999"; - private static final String ROW_COLOR_WARNING = "#ffebcc"; - private static final String ROW_COLOR_HINT = "#ebf5ff"; - public static final int STATUS_OK = 0; - public static final int STATUS_HINT = 1; - public static final int STATUS_WARNING = 2; - public static final int STATUS_ERROR = 3; - public static final int STATUS_FATAL = 4; - - - private static final String DERIVATION_EQUALS = "derivation.equals"; - public static final String DERIVATION_POINTER = "derived.pointer"; - public static final String IS_DERIVED = "derived.fact"; - public static final String UD_ERROR_STATUS = "error-status"; - - // note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here - private final IWorkerContext context; - private List messages; - private List snapshotStack = new ArrayList(); - private ProfileKnowledgeProvider pkp; - - public ProfileUtilities(IWorkerContext context, List messages, ProfileKnowledgeProvider pkp) { - super(); - this.context = context; - this.messages = messages; - this.pkp = pkp; - } - - private class UnusedTracker { - private boolean used; - } - - public interface ProfileKnowledgeProvider { - public class BindingResolution { - public String display; - public String url; - } - boolean isDatatype(String typeSimple); - boolean isResource(String typeSimple); - boolean hasLinkFor(String typeSimple); - String getLinkFor(String typeSimple); - BindingResolution resolveBinding(ElementDefinitionBindingComponent binding); - String getLinkForProfile(StructureDefinition profile, String url); - } - - -/** - * Given a Structure, navigate to the element given by the path and return the direct children of that element - * - * @param structure The structure to navigate into - * @param path The path of the element within the structure to get the children for - * @return A Map containing the name of the element child (not the path) and the child itself (an Element) - * @throws DefinitionException - * @throws Exception - */ - public static List getChildMap(StructureDefinition profile, String name, String path, String nameReference) throws DefinitionException { - List res = new ArrayList(); - - // if we have a name reference, we have to find it, and iterate it's children - if (nameReference != null) { - boolean found = false; - for (ElementDefinition e : profile.getSnapshot().getElement()) { - if (nameReference.equals(e.getName())) { - found = true; - path = e.getPath(); - } - } - if (!found) - throw new DefinitionException("Unable to resolve name reference "+nameReference+" at path "+path); - } - - for (ElementDefinition e : profile.getSnapshot().getElement()) - { - String p = e.getPath(); - - if (path != null && !Utilities.noString(e.getNameReference()) && path.startsWith(p)) - { - /* The path we are navigating to is on or below this element, but the element defers its definition to another named part of the - * structure. - */ - if (path.length() > p.length()) - { - // The path navigates further into the referenced element, so go ahead along the path over there - return getChildMap(profile, name, e.getNameReference()+"."+path.substring(p.length()+1), null); - } - else - { - // The path we are looking for is actually this element, but since it defers it definition, go get the referenced element - return getChildMap(profile, name, e.getNameReference(), null); - } - } - else if (p.startsWith(path+".")) - { - // The path of the element is a child of the path we're looking for (i.e. the parent), - // so add this element to the result. - String tail = p.substring(path.length()+1); - - // Only add direct children, not any deeper paths - if (!tail.contains(".")) { - res.add(e); - } - } - } - - return res; - } - - - public static List getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException { - return getChildMap(profile, element.getName(), element.getPath(), null); - } - - - /** - * Given a Structure, navigate to the element given by the path and return the direct children of that element - * - * @param structure The structure to navigate into - * @param path The path of the element within the structure to get the children for - * @return A List containing the element children (all of them are Elements) - */ - public static List getChildList(StructureDefinition profile, String path) { - List res = new ArrayList(); - - for (ElementDefinition e : profile.getSnapshot().getElement()) - { - String p = e.getPath(); - - if (!Utilities.noString(e.getNameReference()) && path.startsWith(p)) - { - if (path.length() > p.length()) - return getChildList(profile, e.getNameReference()+"."+path.substring(p.length()+1)); - else - return getChildList(profile, e.getNameReference()); - } - else if (p.startsWith(path+".") && !p.equals(path)) - { - String tail = p.substring(path.length()+1); - if (!tail.contains(".")) { - res.add(e); - } - } - - } - - return res; - } - - - public static List getChildList(StructureDefinition structure, ElementDefinition element) { - return getChildList(structure, element.getPath()); - } - - /** - * Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile - * - * @param base - the base structure on which the differential will be applied - * @param differential - the differential to apply to the base - * @param url - where the base has relative urls for profile references, these need to be converted to absolutes by prepending this URL - * @param trimDifferential - if this is true, then the snap short generator will remove any material in the element definitions that is not different to the base - * @return - * @throws FHIRException - * @throws DefinitionException - * @throws Exception - */ - public void generateSnapshot(StructureDefinition base, StructureDefinition derived, String url, String profileName) throws DefinitionException, FHIRException { - if (base == null) - throw new DefinitionException("no base profile provided"); - if (derived == null) - throw new DefinitionException("no derived structure provided"); - - if (snapshotStack.contains(derived.getUrl())) - throw new DefinitionException("Circular snapshot references detected; cannot generate snapshot (stack = "+snapshotStack.toString()+")"); - snapshotStack.add(derived.getUrl()); - -// System.out.println("Generate Snapshot for "+derived.getUrl()); - - derived.setSnapshot(new StructureDefinitionSnapshotComponent()); - - // so we have two lists - the base list, and the differential list - // the differential list is only allowed to include things that are in the base list, but - // is allowed to include them multiple times - thereby slicing them - - // our approach is to walk through the base list, and see whether the differential - // says anything about them. - int baseCursor = 0; - int diffCursor = 0; // we need a diff cursor because we can only look ahead, in the bound scoped by longer paths - - // we actually delegate the work to a subroutine so we can re-enter it with a different cursors - processPaths(derived.getSnapshot(), base.getSnapshot(), derived.getDifferential(), baseCursor, diffCursor, base.getSnapshot().getElement().size()-1, derived.getDifferential().getElement().size()-1, url, derived.getId(), null, false, base.getUrl(), null, false); - } - - /** - * @param trimDifferential - * @throws DefinitionException, FHIRException - * @throws Exception - */ - private void processPaths(StructureDefinitionSnapshotComponent result, StructureDefinitionSnapshotComponent base, StructureDefinitionDifferentialComponent differential, int baseCursor, int diffCursor, int baseLimit, - int diffLimit, String url, String profileName, String contextPath, boolean trimDifferential, String contextName, String resultPathBase, boolean slicingDone) throws DefinitionException, FHIRException { - - // just repeat processing entries until we run out of our allowed scope (1st entry, the allowed scope is all the entries) - while (baseCursor <= baseLimit) { - // get the current focus of the base, and decide what to do - ElementDefinition currentBase = base.getElement().get(baseCursor); - String cpath = fixedPath(contextPath, currentBase.getPath()); - List diffMatches = getDiffMatches(differential, cpath, diffCursor, diffLimit, profileName); // get a list of matching elements in scope - - // in the simple case, source is not sliced. - if (!currentBase.hasSlicing()) { - if (diffMatches.isEmpty()) { // the differential doesn't say anything about this item - // so we just copy it in - ElementDefinition outcome = updateURLs(url, currentBase.copy()); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - updateFromBase(outcome, currentBase); - markDerived(outcome); - if (resultPathBase == null) - resultPathBase = outcome.getPath(); - else if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - result.getElement().add(outcome); - baseCursor++; - } else if (diffMatches.size() == 1 && (slicingDone || (!diffMatches.get(0).hasSlicing() && !(isExtension(diffMatches.get(0)) && !diffMatches.get(0).hasName())))) {// one matching element in the differential - ElementDefinition template = null; - if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !diffMatches.get(0).getType().get(0).getCode().equals("Reference")) { - String p = diffMatches.get(0).getType().get(0).getProfile().get(0).asStringValue(); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); - if (sd != null) { - if (!sd.hasSnapshot()) { - StructureDefinition sdb = context.fetchResource(StructureDefinition.class, sd.getBase()); - if (sdb == null) - throw new DefinitionException("no base for "+sd.getBase()); - generateSnapshot(sdb, sd, sd.getUrl(), sd.getName()); - } - template = sd.getSnapshot().getElement().get(0).copy().setPath(currentBase.getPath()); - // temporary work around - if (!diffMatches.get(0).getType().get(0).getCode().equals("Extension")) { - template.setMin(currentBase.getMin()); - template.setMax(currentBase.getMax()); - } - } - } - if (template == null) - template = currentBase.copy(); - else - // some of what's in currentBase overrides template - template = overWriteWithCurrent(template, currentBase); - ElementDefinition outcome = updateURLs(url, template); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - updateFromBase(outcome, currentBase); - if (diffMatches.get(0).hasName()) - outcome.setName(diffMatches.get(0).getName()); - outcome.setSlicing(null); - updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url); - if (outcome.getPath().endsWith("[x]") && outcome.getType().size() == 1 && !outcome.getType().get(0).getCode().equals("*")) // if the base profile allows multiple types, but the profile only allows one, rename it - outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length()-3)+Utilities.capitalize(outcome.getType().get(0).getCode())); - if (resultPathBase == null) - resultPathBase = outcome.getPath(); - else if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - result.getElement().add(outcome); - baseCursor++; - diffCursor = differential.getElement().indexOf(diffMatches.get(0))+1; - if (differential.getElement().size() > diffCursor && outcome.getPath().contains(".") && isDataType(outcome.getType())) { // don't want to do this for the root, since that's base, and we're already processing it - if (pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+".")) { - if (outcome.getType().size() > 1) - throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") and multiple types ("+typeCode(outcome.getType())+") in profile "+profileName); - StructureDefinition dt = getProfileForDataType(outcome.getType().get(0)); - if (dt == null) - throw new DefinitionException(diffMatches.get(0).getPath()+" has children ("+differential.getElement().get(diffCursor).getPath()+") for type "+typeCode(outcome.getType())+" in profile "+profileName+", but can't find type"); - contextName = dt.getUrl(); - int start = diffCursor; - while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath()+".")) - diffCursor++; - processPaths(result, dt.getSnapshot(), differential, 1 /* starting again on the data type, but skip the root */, start-1, dt.getSnapshot().getElement().size()-1, - diffCursor - 1, url, profileName+pathTail(diffMatches, 0), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false); - } - } - } else { - // ok, the differential slices the item. Let's check our pre-conditions to ensure that this is correct - if (!unbounded(currentBase) && !isSlicedToOneOnly(diffMatches.get(0))) - // you can only slice an element that doesn't repeat if the sum total of your slices is limited to 1 - // (but you might do that in order to split up constraints by type) - throw new DefinitionException("Attempt to a slice an element that does not repeat: "+currentBase.getPath()+"/"+currentBase.getName()+" from "+contextName); - if (!diffMatches.get(0).hasSlicing() && !isExtension(currentBase)) // well, the diff has set up a slice, but hasn't defined it. this is an error - throw new DefinitionException("differential does not have a slice: "+currentBase.getPath()); - - // well, if it passed those preconditions then we slice the dest. - // we're just going to accept the differential slicing at face value - ElementDefinition outcome = updateURLs(url, currentBase.copy()); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - updateFromBase(outcome, currentBase); - - if (!diffMatches.get(0).hasSlicing()) - outcome.setSlicing(makeExtensionSlicing()); - else - outcome.setSlicing(diffMatches.get(0).getSlicing().copy()); - if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - result.getElement().add(outcome); - - // differential - if the first one in the list has a name, we'll process it. Else we'll treat it as the base definition of the slice. - int start = 0; - if (!diffMatches.get(0).hasName()) { - updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url); - if (!outcome.hasType()) { - throw new DefinitionException("not done yet"); - } - start = 1; - } else - checkExtensionDoco(outcome); - - // now, for each entry in the diff matches, we're going to process the base item - // our processing scope for base is all the children of the current path - int nbl = findEndOfElement(base, baseCursor); - int ndc = diffCursor; - int ndl = diffCursor; - for (int i = start; i < diffMatches.size(); i++) { - // our processing scope for the differential is the item in the list, and all the items before the next one in the list - ndc = differential.getElement().indexOf(diffMatches.get(i)); - ndl = findEndOfElement(differential, ndc); - // now we process the base scope repeatedly for each instance of the item in the differential list - processPaths(result, base, differential, baseCursor, ndc, nbl, ndl, url, profileName+pathTail(diffMatches, i), contextPath, trimDifferential, contextName, resultPathBase, true); - } - // ok, done with that - next in the base list - baseCursor = nbl+1; - diffCursor = ndl+1; - } - } else { - // the item is already sliced in the base profile. - // here's the rules - // 1. irrespective of whether the slicing is ordered or not, the definition order must be maintained - // 2. slice element names have to match. - // 3. new slices must be introduced at the end - // corallory: you can't re-slice existing slices. is that ok? - - // we're going to need this: - String path = currentBase.getPath(); - ElementDefinition original = currentBase; - - if (diffMatches.isEmpty()) { // the differential doesn't say anything about this item - // copy across the currentbase, and all of it's children and siblings - while (baseCursor < base.getElement().size() && base.getElement().get(baseCursor).getPath().startsWith(path)) { - ElementDefinition outcome = updateURLs(url, base.getElement().get(baseCursor).copy()); - if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path: "+outcome.getPath()+" vs " + resultPathBase); - result.getElement().add(outcome); // so we just copy it in - baseCursor++; - } - } else { - // first - check that the slicing is ok - boolean closed = currentBase.getSlicing().getRules() == SlicingRules.CLOSED; - int diffpos = 0; - boolean isExtension = cpath.endsWith(".extension") || cpath.endsWith(".modifierExtension"); - if (diffMatches.get(0).hasSlicing()) { // it might be null if the differential doesn't want to say anything about slicing - if (!isExtension) - diffpos++; // if there's a slice on the first, we'll ignore any content it has - ElementDefinitionSlicingComponent dSlice = diffMatches.get(0).getSlicing(); - ElementDefinitionSlicingComponent bSlice = currentBase.getSlicing(); - if (!orderMatches(dSlice.getOrderedElement(), bSlice.getOrderedElement())) - throw new DefinitionException("Slicing rules on differential ("+summariseSlicing(dSlice)+") do not match those on base ("+summariseSlicing(bSlice)+") - order @ "+path+" ("+contextName+")"); - if (!discriiminatorMatches(dSlice.getDiscriminator(), bSlice.getDiscriminator())) - throw new DefinitionException("Slicing rules on differential ("+summariseSlicing(dSlice)+") do not match those on base ("+summariseSlicing(bSlice)+") - disciminator @ "+path+" ("+contextName+")"); - if (!ruleMatches(dSlice.getRules(), bSlice.getRules())) - throw new DefinitionException("Slicing rules on differential ("+summariseSlicing(dSlice)+") do not match those on base ("+summariseSlicing(bSlice)+") - rule @ "+path+" ("+contextName+")"); - } - ElementDefinition outcome = updateURLs(url, currentBase.copy()); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - updateFromBase(outcome, currentBase); - if (diffMatches.get(0).hasSlicing() && !isExtension) { - updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing()); - updateFromDefinition(outcome, diffMatches.get(0), profileName, closed, url); // if there's no slice, we don't want to update the unsliced description - } - if (diffMatches.get(0).hasSlicing() && !diffMatches.get(0).hasName()) - diffpos++; - - result.getElement().add(outcome); - - // now, we have two lists, base and diff. we're going to work through base, looking for matches in diff. - List baseMatches = getSiblings(base.getElement(), currentBase); - for (ElementDefinition baseItem : baseMatches) { - baseCursor = base.getElement().indexOf(baseItem); - outcome = updateURLs(url, baseItem.copy()); - updateFromBase(outcome, currentBase); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - outcome.setSlicing(null); - if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - if (diffpos < diffMatches.size() && diffMatches.get(diffpos).getName().equals(outcome.getName())) { - // if there's a diff, we update the outcome with diff - // no? updateFromDefinition(outcome, diffMatches.get(diffpos), profileName, closed, url); - //then process any children - int nbl = findEndOfElement(base, baseCursor); - int ndc = differential.getElement().indexOf(diffMatches.get(diffpos)); - int ndl = findEndOfElement(differential, ndc); - // now we process the base scope repeatedly for each instance of the item in the differential list - processPaths(result, base, differential, baseCursor, ndc, nbl, ndl, url, profileName+pathTail(diffMatches, diffpos), contextPath, closed, contextName, resultPathBase, true); - // ok, done with that - now set the cursors for if this is the end - baseCursor = nbl+1; - diffCursor = ndl+1; - diffpos++; - } else { - result.getElement().add(outcome); - baseCursor++; - // just copy any children on the base - while (baseCursor < base.getElement().size() && base.getElement().get(baseCursor).getPath().startsWith(path) && !base.getElement().get(baseCursor).getPath().equals(path)) { - outcome = updateURLs(url, currentBase.copy()); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - result.getElement().add(outcome); - baseCursor++; - } - } - } - // finally, we process any remaining entries in diff, which are new (and which are only allowed if the base wasn't closed - if (closed && diffpos < diffMatches.size()) - throw new DefinitionException("The base snapshot marks a slicing as closed, but the differential tries to extend it in "+profileName+" at "+path+" ("+cpath+")"); - while (diffpos < diffMatches.size()) { - ElementDefinition diffItem = diffMatches.get(diffpos); - for (ElementDefinition baseItem : baseMatches) - if (baseItem.getName().equals(diffItem.getName())) - throw new DefinitionException("Named items are out of order in the slice"); - outcome = updateURLs(url, original.copy()); - outcome.setPath(fixedPath(contextPath, outcome.getPath())); - updateFromBase(outcome, currentBase); - outcome.setSlicing(null); - if (!outcome.getPath().startsWith(resultPathBase)) - throw new DefinitionException("Adding wrong path"); - result.getElement().add(outcome); - updateFromDefinition(outcome, diffItem, profileName, trimDifferential, url); - diffpos++; - } - } - } - } - } - - - private ElementDefinition overWriteWithCurrent(ElementDefinition profile, ElementDefinition usage) { - ElementDefinition res = profile.copy(); - if (usage.hasName()) - res.setName(usage.getName()); - if (usage.hasLabel()) - res.setLabel(usage.getLabel()); - for (Coding c : usage.getCode()) - res.addCode(c); - - if (usage.hasDefinition()) - res.setDefinition(usage.getDefinition()); - if (usage.hasShort()) - res.setShort(usage.getShort()); - if (usage.hasComments()) - res.setComments(usage.getComments()); - if (usage.hasRequirements()) - res.setRequirements(usage.getRequirements()); - for (StringType c : usage.getAlias()) - res.addAlias(c.getValue()); - if (usage.hasMin()) - res.setMin(usage.getMin()); - if (usage.hasMax()) - res.setMax(usage.getMax()); - - if (usage.hasFixed()) - res.setFixed(usage.getFixed()); - if (usage.hasPattern()) - res.setPattern(usage.getPattern()); - if (usage.hasExample()) - res.setExample(usage.getExample()); - if (usage.hasMinValue()) - res.setMinValue(usage.getMinValue()); - if (usage.hasMaxValue()) - res.setMaxValue(usage.getMaxValue()); - if (usage.hasMaxLength()) - res.setMaxLength(usage.getMaxLength()); - if (usage.hasMustSupport()) - res.setMustSupport(usage.getMustSupport()); - if (usage.hasBinding()) - res.setBinding(usage.getBinding().copy()); - for (ElementDefinitionConstraintComponent c : usage.getConstraint()) - res.addConstraint(c); - - return res; - } - - - private boolean checkExtensionDoco(ElementDefinition base) { - // see task 3970. For an extension, there's no point copying across all the underlying definitional stuff - boolean isExtension = base.getPath().equals("Extension") || base.getPath().endsWith(".extension") || base.getPath().endsWith(".modifierExtension"); - if (isExtension) { - base.setDefinition("An Extension"); - base.setShort("Extension"); - base.setCommentsElement(null); - base.setRequirementsElement(null); - base.getAlias().clear(); - base.getMapping().clear(); - } - return isExtension; - } - - - private String pathTail(List diffMatches, int i) { - - ElementDefinition d = diffMatches.get(i); - String s = d.getPath().contains(".") ? d.getPath().substring(d.getPath().lastIndexOf(".")+1) : d.getPath(); - return "."+s + (d.hasType() && d.getType().get(0).hasProfile() ? "["+d.getType().get(0).getProfile().get(0).asStringValue()+"]" : ""); - } - - - private void markDerived(ElementDefinition outcome) { - for (ElementDefinitionConstraintComponent inv : outcome.getConstraint()) - inv.setUserData(IS_DERIVED, true); - } - - - private String summariseSlicing(ElementDefinitionSlicingComponent slice) { - StringBuilder b = new StringBuilder(); - boolean first = true; - for (StringType d : slice.getDiscriminator()) { - if (first) - first = false; - else - b.append(", "); - b.append(d); - } - b.append("("); - if (slice.hasOrdered()) - b.append(slice.getOrderedElement().asStringValue()); - b.append("/"); - if (slice.hasRules()) - b.append(slice.getRules().toCode()); - b.append(")"); - if (slice.hasDescription()) { - b.append(" \""); - b.append(slice.getDescription()); - b.append("\""); - } - return b.toString(); - } - - - private void updateFromBase(ElementDefinition derived, ElementDefinition base) { - if (base.hasBase()) { - derived.getBase().setPath(base.getBase().getPath()); - derived.getBase().setMin(base.getBase().getMin()); - derived.getBase().setMax(base.getBase().getMax()); - } else { - derived.getBase().setPath(base.getPath()); - derived.getBase().setMin(base.getMin()); - derived.getBase().setMax(base.getMax()); - } - } - - - private boolean pathStartsWith(String p1, String p2) { - return p1.startsWith(p2); - } - - private boolean pathMatches(String p1, String p2) { - return p1.equals(p2) || (p2.endsWith("[x]") && p1.startsWith(p2.substring(0, p2.length()-3)) && !p1.substring(p2.length()-3).contains(".")); - } - - - private String fixedPath(String contextPath, String pathSimple) { - if (contextPath == null) - return pathSimple; - return contextPath+"."+pathSimple.substring(pathSimple.indexOf(".")+1); - } - - - private StructureDefinition getProfileForDataType(TypeRefComponent type) { - StructureDefinition sd = null; - if (type.hasProfile()) - sd = context.fetchResource(StructureDefinition.class, type.getProfile().get(0).asStringValue()); - if (sd == null) - sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+type.getCode()); - if (sd == null) - System.out.println("XX: failed to find profle for type: " + type.getCode()); // debug GJM - return sd; - } - - - public static String typeCode(List types) { - StringBuilder b = new StringBuilder(); - boolean first = true; - for (TypeRefComponent type : types) { - if (first) first = false; else b.append(", "); - b.append(type.getCode()); - if (type.hasProfile()) - b.append("{"+type.getProfile()+"}"); - } - return b.toString(); - } - - - private boolean isDataType(List types) { - if (types.isEmpty()) - return false; - for (TypeRefComponent type : types) { - String t = type.getCode(); - if (!isDataType(t) && !t.equals("Reference") && !t.equals("Narrative") && !t.equals("Extension") && !t.equals("ElementDefinition") && !isPrimitive(t)) - return false; - } - return true; - } - - - /** - * Finds internal references in an Element's Binding and StructureDefinition references (in TypeRef) and bases them on the given url - * @param url - the base url to use to turn internal references into absolute references - * @param element - the Element to update - * @return - the updated Element - */ - private ElementDefinition updateURLs(String url, ElementDefinition element) { - if (element != null) { - ElementDefinition defn = element; - if (defn.hasBinding() && defn.getBinding().getValueSet() instanceof Reference && ((Reference)defn.getBinding().getValueSet()).getReference().startsWith("#")) - ((Reference)defn.getBinding().getValueSet()).setReference(url+((Reference)defn.getBinding().getValueSet()).getReference()); - for (TypeRefComponent t : defn.getType()) { - for (UriType tp : t.getProfile()) { - if (tp.getValue().startsWith("#")) - tp.setValue(url+t.getProfile()); - } - } - } - return element; - } - - private List getSiblings(List list, ElementDefinition current) { - List result = new ArrayList(); - String path = current.getPath(); - int cursor = list.indexOf(current)+1; - while (cursor < list.size() && list.get(cursor).getPath().length() >= path.length()) { - if (pathMatches(list.get(cursor).getPath(), path)) - result.add(list.get(cursor)); - cursor++; - } - return result; - } - - private void updateFromSlicing(ElementDefinitionSlicingComponent dst, ElementDefinitionSlicingComponent src) { - if (src.hasOrderedElement()) - dst.setOrderedElement(src.getOrderedElement().copy()); - if (src.hasDiscriminator()) - dst.getDiscriminator().addAll(src.getDiscriminator()); - if (src.hasRulesElement()) - dst.setRulesElement(src.getRulesElement().copy()); - } - - private boolean orderMatches(BooleanType diff, BooleanType base) { - return (diff == null) || (base == null) || (diff.getValue() == base.getValue()); - } - - private boolean discriiminatorMatches(List diff, List base) { - if (diff.isEmpty() || base.isEmpty()) - return true; - if (diff.size() != base.size()) - return false; - for (int i = 0; i < diff.size(); i++) - if (!diff.get(i).getValue().equals(base.get(i).getValue())) - return false; - return true; - } - - private boolean ruleMatches(SlicingRules diff, SlicingRules base) { - return (diff == null) || (base == null) || (diff == base) || (diff == SlicingRules.OPEN) || - ((diff == SlicingRules.OPENATEND && base == SlicingRules.CLOSED)); - } - - private boolean isSlicedToOneOnly(ElementDefinition e) { - return (e.hasSlicing() && e.hasMaxElement() && e.getMax().equals("1")); - } - - private ElementDefinitionSlicingComponent makeExtensionSlicing() { - ElementDefinitionSlicingComponent slice = new ElementDefinitionSlicingComponent(); - slice.addDiscriminator("url"); - slice.setOrdered(false); - slice.setRules(SlicingRules.OPEN); - return slice; - } - - private boolean isExtension(ElementDefinition currentBase) { - return currentBase.getPath().endsWith(".extension") || currentBase.getPath().endsWith(".modifierExtension"); - } - - private List getDiffMatches(StructureDefinitionDifferentialComponent context, String path, int start, int end, String profileName) { - List result = new ArrayList(); - for (int i = start; i <= end; i++) { - String statedPath = context.getElement().get(i).getPath(); - if (statedPath.equals(path) || (path.endsWith("[x]") && statedPath.length() > path.length() - 2 && statedPath.substring(0, path.length()-3).equals(path.substring(0, path.length()-3)) && !statedPath.substring(path.length()).contains("."))) { - result.add(context.getElement().get(i)); - } else if (result.isEmpty()) { -// System.out.println("ignoring "+statedPath+" in differential of "+profileName); - } - } - return result; - } - - private int findEndOfElement(StructureDefinitionDifferentialComponent context, int cursor) { - int result = cursor; - String path = context.getElement().get(cursor).getPath()+"."; - while (result < context.getElement().size()- 1 && context.getElement().get(result+1).getPath().startsWith(path)) - result++; - return result; - } - - private int findEndOfElement(StructureDefinitionSnapshotComponent context, int cursor) { - int result = cursor; - String path = context.getElement().get(cursor).getPath()+"."; - while (result < context.getElement().size()- 1 && context.getElement().get(result+1).getPath().startsWith(path)) - result++; - return result; - } - - private boolean unbounded(ElementDefinition definition) { - StringType max = definition.getMaxElement(); - if (max == null) - return false; // this is not valid - if (max.getValue().equals("1")) - return false; - if (max.getValue().equals("0")) - return false; - return true; - } - - private void updateFromDefinition(ElementDefinition dest, ElementDefinition source, String pn, boolean trimDifferential, String purl) throws DefinitionException, FHIRException { - // we start with a clone of the base profile ('dest') and we copy from the profile ('source') - // over the top for anything the source has - ElementDefinition base = dest; - ElementDefinition derived = source; - derived.setUserData(DERIVATION_POINTER, base); - - if (derived != null) { - boolean isExtension = checkExtensionDoco(base); - - if (derived.hasShortElement()) { - if (!Base.compareDeep(derived.getShortElement(), base.getShortElement(), false)) - base.setShortElement(derived.getShortElement().copy()); - else if (trimDifferential) - derived.setShortElement(null); - else if (derived.hasShortElement()) - derived.getShortElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasDefinitionElement()) { - if (derived.getDefinition().startsWith("...")) - base.setDefinition(base.getDefinition()+"\r\n"+derived.getDefinition().substring(3)); - else if (!Base.compareDeep(derived.getDefinitionElement(), base.getDefinitionElement(), false)) - base.setDefinitionElement(derived.getDefinitionElement().copy()); - else if (trimDifferential) - derived.setDefinitionElement(null); - else if (derived.hasDefinitionElement()) - derived.getDefinitionElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasCommentsElement()) { - if (derived.getComments().startsWith("...")) - base.setComments(base.getComments()+"\r\n"+derived.getComments().substring(3)); - else if (!Base.compareDeep(derived.getCommentsElement(), base.getCommentsElement(), false)) - base.setCommentsElement(derived.getCommentsElement().copy()); - else if (trimDifferential) - base.setCommentsElement(derived.getCommentsElement().copy()); - else if (derived.hasCommentsElement()) - derived.getCommentsElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasLabelElement()) { - if (derived.getLabel().startsWith("...")) - base.setLabel(base.getLabel()+"\r\n"+derived.getLabel().substring(3)); - else if (!Base.compareDeep(derived.getLabelElement(), base.getLabelElement(), false)) - base.setLabelElement(derived.getLabelElement().copy()); - else if (trimDifferential) - base.setLabelElement(derived.getLabelElement().copy()); - else if (derived.hasLabelElement()) - derived.getLabelElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasRequirementsElement()) { - if (derived.getRequirements().startsWith("...")) - base.setRequirements(base.getRequirements()+"\r\n"+derived.getRequirements().substring(3)); - else if (!Base.compareDeep(derived.getRequirementsElement(), base.getRequirementsElement(), false)) - base.setRequirementsElement(derived.getRequirementsElement().copy()); - else if (trimDifferential) - base.setRequirementsElement(derived.getRequirementsElement().copy()); - else if (derived.hasRequirementsElement()) - derived.getRequirementsElement().setUserData(DERIVATION_EQUALS, true); - } - // sdf-9 - if (derived.hasRequirements() && !base.getPath().contains(".")) - derived.setRequirements(null); - if (base.hasRequirements() && !base.getPath().contains(".")) - base.setRequirements(null); - - if (derived.hasAlias()) { - if (!Base.compareDeep(derived.getAlias(), base.getAlias(), false)) - for (StringType s : derived.getAlias()) { - if (!base.hasAlias(s.getValue())) - base.getAlias().add(s.copy()); - } - else if (trimDifferential) - derived.getAlias().clear(); - else - for (StringType t : derived.getAlias()) - t.setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasMinElement()) { - if (!Base.compareDeep(derived.getMinElement(), base.getMinElement(), false)) { - if (derived.getMin() < base.getMin()) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Derived min ("+Integer.toString(derived.getMin())+") cannot be less than base min ("+Integer.toString(base.getMin())+")", IssueSeverity.ERROR)); - base.setMinElement(derived.getMinElement().copy()); - } else if (trimDifferential) - derived.setMinElement(null); - else - derived.getMinElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasMaxElement()) { - if (!Base.compareDeep(derived.getMaxElement(), base.getMaxElement(), false)) { - if (isLargerMax(derived.getMax(), base.getMax())) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Derived max ("+derived.getMax()+") cannot be greater than base max ("+base.getMax()+")", IssueSeverity.ERROR)); - base.setMaxElement(derived.getMaxElement().copy()); - } else if (trimDifferential) - derived.setMaxElement(null); - else - derived.getMaxElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasFixed()) { - if (!Base.compareDeep(derived.getFixed(), base.getFixed(), true)) { - base.setFixed(derived.getFixed().copy()); - } else if (trimDifferential) - derived.setFixed(null); - else - derived.getFixed().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasPattern()) { - if (!Base.compareDeep(derived.getPattern(), base.getPattern(), false)) { - base.setPattern(derived.getPattern().copy()); - } else - if (trimDifferential) - derived.setPattern(null); - else - derived.getPattern().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasExample()) { - if (!Base.compareDeep(derived.getExample(), base.getExample(), false)) - base.setExample(derived.getExample().copy()); - else if (trimDifferential) - derived.setExample(null); - else - derived.getExample().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasMaxLengthElement()) { - if (!Base.compareDeep(derived.getMaxLengthElement(), base.getMaxLengthElement(), false)) - base.setMaxLengthElement(derived.getMaxLengthElement().copy()); - else if (trimDifferential) - derived.setMaxLengthElement(null); - else - derived.getMaxLengthElement().setUserData(DERIVATION_EQUALS, true); - } - - // todo: what to do about conditions? - // condition : id 0..* - - if (derived.hasMustSupportElement()) { - if (!Base.compareDeep(derived.getMustSupportElement(), base.getMustSupportElement(), false)) - base.setMustSupportElement(derived.getMustSupportElement().copy()); - else if (trimDifferential) - derived.setMustSupportElement(null); - else - derived.getMustSupportElement().setUserData(DERIVATION_EQUALS, true); - } - - - // profiles cannot change : isModifier, defaultValue, meaningWhenMissing - // but extensions can change isModifier - if (isExtension) { - if (!Base.compareDeep(derived.getIsModifierElement(), base.getIsModifierElement(), false)) - base.setIsModifierElement(derived.getIsModifierElement().copy()); - else if (trimDifferential) - derived.setIsModifierElement(null); - else - derived.getIsModifierElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasBinding()) { - if (!Base.compareDeep(derived.getBinding(), base.getBinding(), false)) { - if (base.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && derived.getBinding().getStrength() != BindingStrength.REQUIRED) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change a binding from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), IssueSeverity.ERROR)); -// throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": illegal attempt to change a binding from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode()); - else if (base.hasBinding() && derived.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED) { - ValueSetExpansionOutcome expBase = context.expandVS(context.fetchResource(ValueSet.class, base.getBinding().getValueSetReference().getReference()), true); - ValueSetExpansionOutcome expDerived = context.expandVS(context.fetchResource(ValueSet.class, derived.getBinding().getValueSetReference().getReference()), true); - if (expBase.getValueset() == null) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSetReference().getReference()+" could not be expanded", IssueSeverity.WARNING)); - else if (expDerived.getValueset() == null) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSetReference().getReference()+" could not be expanded", IssueSeverity.WARNING)); - else if (!isSubset(expBase.getValueset(), expDerived.getValueset())) - messages.add(new ValidationMessage(Source.ProfileValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSetReference().getReference()+" is not a subset of binding "+base.getBinding().getValueSetReference().getReference(), IssueSeverity.ERROR)); - } - base.setBinding(derived.getBinding().copy()); - } else if (trimDifferential) - derived.setBinding(null); - else - derived.getBinding().setUserData(DERIVATION_EQUALS, true); - } // else if (base.hasBinding() && doesn't have bindable type ) - // base - - if (derived.hasIsSummaryElement()) { - if (!Base.compareDeep(derived.getIsSummaryElement(), base.getIsSummaryElement(), false)) - base.setIsSummaryElement(derived.getIsSummaryElement().copy()); - else if (trimDifferential) - derived.setIsSummaryElement(null); - else - derived.getIsSummaryElement().setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasType()) { - if (!Base.compareDeep(derived.getType(), base.getType(), false)) { - if (base.hasType()) { - for (TypeRefComponent ts : derived.getType()) { - boolean ok = false; - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (TypeRefComponent td : base.getType()) { - b.append(td.getCode()); - if (td.hasCode() && (td.getCode().equals(ts.getCode()) || td.getCode().equals("Extension") || - td.getCode().equals("Element") || td.getCode().equals("*") || - ((td.getCode().equals("Resource") || (td.getCode().equals("DomainResource")) && pkp.isResource(ts.getCode()))))) - ok = true; - } - if (!ok) - throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": illegal constrained type "+ts.getCode()+" from "+b.toString()); - } - } - base.getType().clear(); - for (TypeRefComponent t : derived.getType()) { - TypeRefComponent tt = t.copy(); -// tt.setUserData(DERIVATION_EQUALS, true); - base.getType().add(tt); - } - } - else if (trimDifferential) - derived.getType().clear(); - else - for (TypeRefComponent t : derived.getType()) - t.setUserData(DERIVATION_EQUALS, true); - } - - if (derived.hasMapping()) { - // todo: mappings are not cumulative - one replaces another - if (!Base.compareDeep(derived.getMapping(), base.getMapping(), false)) { - for (ElementDefinitionMappingComponent s : derived.getMapping()) { - boolean found = false; - for (ElementDefinitionMappingComponent d : base.getMapping()) { - found = found || (d.getIdentity().equals(s.getIdentity()) && d.getMap().equals(s.getMap())); - } - if (!found) - base.getMapping().add(s); - } - } - else if (trimDifferential) - derived.getMapping().clear(); - else - for (ElementDefinitionMappingComponent t : derived.getMapping()) - t.setUserData(DERIVATION_EQUALS, true); - } - - // todo: constraints are cumulative. there is no replacing - for (ElementDefinitionConstraintComponent s : base.getConstraint()) - s.setUserData(IS_DERIVED, true); - if (derived.hasConstraint()) { - for (ElementDefinitionConstraintComponent s : derived.getConstraint()) { - base.getConstraint().add(s.copy()); - } - } - } - } - - private boolean isLargerMax(String derived, String base) { - if ("*".equals(base)) - return false; - if ("*".equals(derived)) - return true; - return Integer.parseInt(derived) > Integer.parseInt(base); - } - - - private boolean isSubset(ValueSet expBase, ValueSet expDerived) { - return codesInExpansion(expDerived.getExpansion().getContains(), expBase.getExpansion()); - } - - - private boolean codesInExpansion(List contains, ValueSetExpansionComponent expansion) { - for (ValueSetExpansionContainsComponent cc : contains) { - if (!inExpansion(cc, expansion.getContains())) - return false; - if (!codesInExpansion(cc.getContains(), expansion)) - return false; - } - return true; - } - - - private boolean inExpansion(ValueSetExpansionContainsComponent cc, List contains) { - for (ValueSetExpansionContainsComponent cc1 : contains) { - if (cc.getSystem().equals(cc1.getSystem()) && cc.getCode().equals(cc1.getCode())) - return true; - if (inExpansion(cc, cc1.getContains())) - return true; - } - return false; - } - - - public XhtmlNode generateExtensionTable(String defFile, StructureDefinition ed, String imageFolder, boolean inlineGraphics, boolean full, String corePath) throws IOException, FHIRException { - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics); - TableModel model = gen.initNormalTable(corePath, false); - - boolean deep = false; - boolean vdeep = false; - for (ElementDefinition eld : ed.getSnapshot().getElement()) { - deep = deep || eld.getPath().contains("Extension.extension."); - vdeep = vdeep || eld.getPath().contains("Extension.extension.extension."); - } - Row r = gen.new Row(); - model.getRows().add(r); - r.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), ed.getSnapshot().getElement().get(0).getIsModifier() ? "modifierExtension" : "extension", null, null)); - r.getCells().add(gen.new Cell()); - r.getCells().add(gen.new Cell(null, null, describeCardinality(ed.getSnapshot().getElement().get(0), null, new UnusedTracker()), null, null)); - - if (full || vdeep) { - r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - - r.setIcon(deep ? "icon_extension_complex.png" : "icon_extension_simple.png", deep ? HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX : HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - List children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); - for (ElementDefinition child : children) - if (!child.getPath().endsWith(".id")) - genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), null, true, defFile, true, full, corePath); - } else if (deep) { - List children = new ArrayList(); - for (ElementDefinition ted : ed.getSnapshot().getElement()) { - if (ted.getPath().equals("Extension.extension")) - children.add(ted); - } - - r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - r.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - - for (ElementDefinition c : children) { - ElementDefinition ved = getValueFor(ed, c); - ElementDefinition ued = getUrlFor(ed, c); - if (ved != null && ued != null) { - Row r1 = gen.new Row(); - r.getSubRows().add(r1); - r1.getCells().add(gen.new Cell(null, defFile == null ? "" : defFile+"-definitions.html#extension."+ed.getName(), ((UriType) ued.getFixed()).getValue(), null, null)); - r1.getCells().add(gen.new Cell()); - r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); - genTypes(gen, r1, ved, defFile, ed, corePath); - r1.getCells().add(gen.new Cell(null, null, c.getDefinition(), null, null)); - r1.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - } - } - } else { - ElementDefinition ved = null; - for (ElementDefinition ted : ed.getSnapshot().getElement()) { - if (ted.getPath().startsWith("Extension.value")) - ved = ted; - } - - genTypes(gen, r, ved, defFile, ed, corePath); - - r.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - } - Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); - c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, ed.getName()+": "+ed.getDescription(), null)); - c.addPiece(gen.new Piece("br")).addPiece(gen.new Piece(null, describeExtensionContext(ed), null)); - r.getCells().add(c); - - - try { - return gen.generate(model, corePath); - } catch (org.hl7.fhir.exceptions.FHIRException e) { - throw new FHIRException(e.getMessage(), e); - } - } - - private ElementDefinition getUrlFor(StructureDefinition ed, ElementDefinition c) { - int i = ed.getSnapshot().getElement().indexOf(c) + 1; - while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { - if (ed.getSnapshot().getElement().get(i).getPath().equals(c.getPath()+".url")) - return ed.getSnapshot().getElement().get(i); - i++; - } - return null; - } - - private ElementDefinition getValueFor(StructureDefinition ed, ElementDefinition c) { - int i = ed.getSnapshot().getElement().indexOf(c) + 1; - while (i < ed.getSnapshot().getElement().size() && ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".")) { - if (ed.getSnapshot().getElement().get(i).getPath().startsWith(c.getPath()+".value")) - return ed.getSnapshot().getElement().get(i); - i++; - } - return null; - } - - - private Cell genTypes(HierarchicalTableGenerator gen, Row r, ElementDefinition e, String profileBaseFileName, StructureDefinition profile, String corePath) { - Cell c = gen.new Cell(); - r.getCells().add(c); - List types = e.getType(); - if (!e.hasType()) { - if (e.hasNameReference()) { - ElementDefinition ed = getElementByName(profile.getSnapshot().getElement(), e.getNameReference()); - if (ed == null) - c.getPieces().add(gen.new Piece(null, "Unknown reference to "+e.getNameReference(), null)); - else - c.getPieces().add(gen.new Piece("#"+ed.getPath(), "See "+ed.getPath(), null)); - return c; - } else { - ElementDefinition d = (ElementDefinition) e.getUserData(DERIVATION_POINTER); - if (d != null && d.hasType()) { - types = new ArrayList(); - for (TypeRefComponent tr : d.getType()) { - TypeRefComponent tt = tr.copy(); - tt.setUserData(DERIVATION_EQUALS, true); - types.add(tt); - } - } else - return c; - } - } - - boolean first = true; - Element source = types.get(0); // either all types are the same, or we don't consider any of them the same - - boolean allReference = ADD_REFERENCE_TO_TABLE && !types.isEmpty(); - for (TypeRefComponent t : types) { - if (!(t.getCode().equals("Reference") && t.hasProfile())) - allReference = false; - } - if (allReference) { - c.getPieces().add(gen.new Piece(corePath+"references.html", "Reference", null)); - c.getPieces().add(gen.new Piece(null, "(", null)); - } - TypeRefComponent tl = null; - for (TypeRefComponent t : types) { - if (first) - first = false; - else if (allReference) - c.addPiece(checkForNoChange(tl, gen.new Piece(null," | ", null))); - else - c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); - tl = t; - if (t.getCode().equals("Reference") || (t.getCode().equals("Resource") && t.hasProfile())) { - if (ADD_REFERENCE_TO_TABLE && !allReference) { - c.getPieces().add(gen.new Piece(corePath+"references.html", "Reference", null)); - c.getPieces().add(gen.new Piece(null, "(", null)); - } - if (t.hasProfile() && t.getProfile().get(0).getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) { - StructureDefinition sd = context.fetchResource(StructureDefinition.class, t.getProfile().get(0).getValue()); - if (sd != null) { - String disp = sd.hasDisplay() ? sd.getDisplay() : sd.getName(); - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+sd.getUserString("path"), disp, null))); - } else { - String rn = t.getProfile().get(0).getValue().substring(40); - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+pkp.getLinkFor(rn), rn, null))); - } - } else if (t.getProfile().size() == 0) { - c.addPiece(checkForNoChange(t, gen.new Piece(null, t.getCode(), null))); - } else if (t.getProfile().get(0).getValue().startsWith("#")) - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+profileBaseFileName+"."+t.getProfile().get(0).getValue().substring(1).toLowerCase()+".html", t.getProfile().get(0).getValue(), null))); - else - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+t.getProfile().get(0).getValue(), t.getProfile().get(0).getValue(), null))); - if (ADD_REFERENCE_TO_TABLE && !allReference) { - c.getPieces().add(gen.new Piece(null, ")", null)); - } - } else if (t.hasProfile()) { // a profiled type - String ref; - ref = pkp.getLinkForProfile(profile, t.getProfile().get(0).getValue()); - if (ref != null) { - String[] parts = ref.split("\\|"); - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+parts[0], parts[1], t.getCode()))); - } else - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+ref, t.getCode(), null))); - } else if (pkp.hasLinkFor(t.getCode())) { - c.addPiece(checkForNoChange(t, gen.new Piece(corePath+pkp.getLinkFor(t.getCode()), t.getCode(), null))); - } else - c.addPiece(checkForNoChange(t, gen.new Piece(null, t.getCode(), null))); - } - if (allReference) { - c.getPieces().add(gen.new Piece(null, ")", null)); - } - return c; - } - - private ElementDefinition getElementByName(List elements, String nameReference) { - for (ElementDefinition ed : elements) - if (ed.hasName() && ed.getName().equals(nameReference)) - return ed; - return null; - } - - - public static String describeExtensionContext(StructureDefinition ext) { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (StringType t : ext.getContext()) - b.append(t.getValue()); - if (!ext.hasContextType()) - throw new Error("no context type on "+ext.getUrl()); - switch (ext.getContextType()) { - case DATATYPE: return "Use on data type: "+b.toString(); - case EXTENSION: return "Use on extension: "+b.toString(); - case RESOURCE: return "Use on element: "+b.toString(); - case MAPPING: return "Use where element has mapping: "+b.toString(); - default: - return "??"; - } - } - - private String describeCardinality(ElementDefinition definition, ElementDefinition fallback, UnusedTracker tracker) { - IntegerType min = definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); - StringType max = definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); - if (min.isEmpty() && fallback != null) - min = fallback.getMinElement(); - if (max.isEmpty() && fallback != null) - max = fallback.getMaxElement(); - - tracker.used = !max.isEmpty() && !max.getValue().equals("0"); - - if (min.isEmpty() && max.isEmpty()) - return null; - else - return (!min.hasValue() ? "" : Integer.toString(min.getValue())) + ".." + (!max.hasValue() ? "" : max.getValue()); - } - - private void genCardinality(HierarchicalTableGenerator gen, ElementDefinition definition, Row row, boolean hasDef, UnusedTracker tracker, ElementDefinition fallback) { - IntegerType min = !hasDef ? new IntegerType() : definition.hasMinElement() ? definition.getMinElement() : new IntegerType(); - StringType max = !hasDef ? new StringType() : definition.hasMaxElement() ? definition.getMaxElement() : new StringType(); - if (min.isEmpty() && definition.getUserData(DERIVATION_POINTER) != null) { - ElementDefinition base = (ElementDefinition) definition.getUserData(DERIVATION_POINTER); - min = base.getMinElement().copy(); - min.setUserData(DERIVATION_EQUALS, true); - } - if (max.isEmpty() && definition.getUserData(DERIVATION_POINTER) != null) { - ElementDefinition base = (ElementDefinition) definition.getUserData(DERIVATION_POINTER); - max = base.getMaxElement().copy(); - max.setUserData(DERIVATION_EQUALS, true); - } - if (min.isEmpty() && fallback != null) - min = fallback.getMinElement(); - if (max.isEmpty() && fallback != null) - max = fallback.getMaxElement(); - - if (!max.isEmpty()) - tracker.used = !max.getValue().equals("0"); - - Cell cell = gen.new Cell(null, null, null, null, null); - row.getCells().add(cell); - if (!min.isEmpty() || !max.isEmpty()) { - cell.addPiece(checkForNoChange(min, gen.new Piece(null, !min.hasValue() ? "" : Integer.toString(min.getValue()), null))); - cell.addPiece(checkForNoChange(min, max, gen.new Piece(null, "..", null))); - cell.addPiece(checkForNoChange(min, gen.new Piece(null, !max.hasValue() ? "" : max.getValue(), null))); - } - } - - - private Piece checkForNoChange(Element source, Piece piece) { - if (source.hasUserData(DERIVATION_EQUALS)) { - piece.addStyle("opacity: 0.4"); - } - return piece; - } - - private Piece checkForNoChange(Element src1, Element src2, Piece piece) { - if (src1.hasUserData(DERIVATION_EQUALS) && src2.hasUserData(DERIVATION_EQUALS)) { - piece.addStyle("opacity: 0.5"); - } - return piece; - } - - public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath) throws IOException, FHIRException { - assert(diff != snapshot);// check it's ok to get rid of one of these - HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics); - TableModel model = gen.initNormalTable(corePath, false); - List list = diff ? profile.getDifferential().getElement() : profile.getSnapshot().getElement(); - List profiles = new ArrayList(); - profiles.add(profile); - genElement(defFile == null ? null : defFile+"#"+profile.getId()+".", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath); - try { - return gen.generate(model, corePath); - } catch (org.hl7.fhir.exceptions.FHIRException e) { - throw new FHIRException(e.getMessage(), e); - } - } - - private void genElement(String defPath, HierarchicalTableGenerator gen, List rows, ElementDefinition element, List all, List profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, boolean snapshot, String corePath) throws IOException { - StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); - String s = tail(element.getPath()); - List children = getChildren(all, element); - boolean isExtension = (s.equals("extension") || s.equals("modifierExtension")); - if (!snapshot && extensions != null && extensions != isExtension) - return; - - if (!onlyInformationIsMapping(all, element)) { - Row row = gen.new Row(); - row.setAnchor(element.getPath()); - row.setColor(getRowColor(element)); - boolean hasDef = element != null; - boolean ext = false; - if (s.equals("extension") || s.equals("modifierExtension")) { - if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) - row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); - else - row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); - ext = true; - } else if (!hasDef || element.getType().size() == 0) - row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - else if (hasDef && element.getType().size() > 1) { - if (allTypesAre(element.getType(), "Reference")) - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - else - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); - } else if (hasDef && element.getType().get(0).getCode() != null && element.getType().get(0).getCode().startsWith("@")) - row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); - else if (hasDef && isPrimitive(element.getType().get(0).getCode())) - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); - else if (hasDef && isReference(element.getType().get(0).getCode())) - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); - else if (hasDef && isDataType(element.getType().get(0).getCode())) - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); - else - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - String ref = defPath == null ? null : defPath + makePathLink(element); - UnusedTracker used = new UnusedTracker(); - used.used = true; - Cell left = gen.new Cell(null, ref, s, !hasDef ? null : element.getDefinition(), null); - row.getCells().add(left); - Cell gc = gen.new Cell(); - row.getCells().add(gc); - if (element != null && element.getIsModifier()) - checkForNoChange(element.getIsModifierElement(), gc.addImage(corePath+"modifier.png", "This element is a modifier element", "?!")); - if (element != null && element.getMustSupport()) - checkForNoChange(element.getMustSupportElement(), gc.addImage(corePath+"mustsupport.png", "This element must be supported", "S")); - if (element != null && element.getIsSummary()) - checkForNoChange(element.getIsSummaryElement(), gc.addImage(corePath+"summary.png", "This element is included in summaries", "∑")); - if (element != null && (!element.getConstraint().isEmpty() || !element.getCondition().isEmpty())) - gc.addImage(corePath+"lock.png", "This element has or is affected by some invariants", "I"); - - ExtensionContext extDefn = null; - if (ext) { - if (element != null && element.getType().size() == 1 && element.getType().get(0).hasProfile()) { - extDefn = locateExtension(StructureDefinition.class, element.getType().get(0).getProfile().get(0).getValue()); - if (extDefn == null) { - genCardinality(gen, element, row, hasDef, used, null); - row.getCells().add(gen.new Cell(null, null, "?? "+element.getType().get(0).getProfile(), null, null)); - generateDescription(gen, row, element, null, used.used, profile.getUrl(), element.getType().get(0).getProfile().get(0).getValue(), profile, corePath); - } else { - String name = urltail(element.getType().get(0).getProfile().get(0).getValue()); - left.getPieces().get(0).setText(name); - // left.getPieces().get(0).setReference((String) extDefn.getExtensionStructure().getTag("filename")); - left.getPieces().get(0).setHint("Extension URL = "+extDefn.getUrl()); - genCardinality(gen, element, row, hasDef, used, extDefn.getElement()); - ElementDefinition valueDefn = extDefn.getExtensionValueDefinition(); - if (valueDefn != null && !"0".equals(valueDefn.getMax())) - genTypes(gen, row, valueDefn, profileBaseFileName, profile, corePath); - else // if it's complex, we just call it nothing - // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); - row.getCells().add(gen.new Cell(null, null, "(Complex)", null, null)); - generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath); - } - } else { - genCardinality(gen, element, row, hasDef, used, null); - if ("0".equals(element.getMax())) - row.getCells().add(gen.new Cell()); - else - genTypes(gen, row, element, profileBaseFileName, profile, corePath); - generateDescription(gen, row, element, null, used.used, null, null, profile, corePath); - } - } else { - genCardinality(gen, element, row, hasDef, used, null); - if (hasDef && !"0".equals(element.getMax())) - genTypes(gen, row, element, profileBaseFileName, profile, corePath); - else - row.getCells().add(gen.new Cell()); - generateDescription(gen, row, element, null, used.used, null, null, profile, corePath); - } - if (element.hasSlicing()) { - if (standardExtensionSlicing(element)) { - used.used = element.hasType() && element.getType().get(0).hasProfile(); - showMissing = false; - } else { - row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); - row.getCells().get(2).getPieces().clear(); - for (Cell cell : row.getCells()) - for (Piece p : cell.getPieces()) { - p.addStyle("font-style: italic"); - } - } - } - if (used.used || showMissing) - rows.add(row); - if (!used.used && !element.hasSlicing()) { - for (Cell cell : row.getCells()) - for (Piece p : cell.getPieces()) { - p.setStyle("text-decoration:line-through"); - p.setReference(null); - } - } else{ - for (ElementDefinition child : children) - if (!child.getPath().endsWith(".id")) - genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath); - if (!snapshot && (extensions == null || !extensions)) - for (ElementDefinition child : children) - if (child.getPath().endsWith(".extension")) - genElement(defPath, gen, row.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, true, false, corePath); - } - } - } - - private ExtensionContext locateExtension(Class class1, String value) { - if (value.contains("#")) { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); - if (ext == null) - return null; - String tail = value.substring(value.indexOf("#")+1); - ElementDefinition ed = null; - for (ElementDefinition ted : ext.getSnapshot().getElement()) { - if (tail.equals(ted.getName())) { - ed = ted; - return new ExtensionContext(ext, ed); - } - } - return null; - } else { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value); - if (ext == null) - return null; - else - return new ExtensionContext(ext, ext.getSnapshot().getElement().get(0)); - } - } - - - private boolean extensionIsComplex(String value) { - if (value.contains("#")) { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value.substring(0, value.indexOf("#"))); - if (ext == null) - return false; - String tail = value.substring(value.indexOf("#")+1); - ElementDefinition ed = null; - for (ElementDefinition ted : ext.getSnapshot().getElement()) { - if (tail.equals(ted.getName())) { - ed = ted; - break; - } - } - if (ed == null) - return false; - int i = ext.getSnapshot().getElement().indexOf(ed); - int j = i+1; - while (j < ext.getSnapshot().getElement().size() && !ext.getSnapshot().getElement().get(j).getPath().equals(ed.getPath())) - j++; - return j - i > 5; - } else { - StructureDefinition ext = context.fetchResource(StructureDefinition.class, value); - return ext != null && ext.getSnapshot().getElement().size() > 5; - } - } - - - private String getRowColor(ElementDefinition element) { - switch (element.getUserInt(UD_ERROR_STATUS)) { - case STATUS_OK: return null; - case STATUS_HINT: return ROW_COLOR_HINT; - case STATUS_WARNING: return ROW_COLOR_WARNING; - case STATUS_ERROR: return ROW_COLOR_ERROR; - case STATUS_FATAL: return ROW_COLOR_FATAL; - default: return null; - } - } - - - private String urltail(String path) { - if (path.contains("#")) - return path.substring(path.lastIndexOf('#')+1); - if (path.contains("/")) - return path.substring(path.lastIndexOf('/')+1); - else - return path; - - } - - private boolean standardExtensionSlicing(ElementDefinition element) { - String t = tail(element.getPath()); - return (t.equals("extension") || t.equals("modifierExtension")) - && element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getValue().equals("url"); - } - - - private String makePathLink(ElementDefinition element) { - if (!element.hasName()) - return element.getPath(); - if (!element.getPath().contains(".")) - return element.getName(); - return element.getPath().substring(0, element.getPath().lastIndexOf("."))+"."+element.getName(); - - } - - private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath) throws IOException { - Cell c = gen.new Cell(); - row.getCells().add(c); - - if (used) { - if (definition.getPath().endsWith("url") && definition.hasFixed()) { - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); - } else { - if (definition != null && definition.hasShort()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, definition.getShort(), null))); - } else if (fallback != null && fallback != null && fallback.hasShort()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.addPiece(checkForNoChange(fallback.getShortElement(), gen.new Piece(null, fallback.getShort(), null))); - } - if (url != null) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - String fullUrl = url.startsWith("#") ? baseURL+url : url; - StructureDefinition ed = context.fetchResource(StructureDefinition.class, url); - String ref = ed == null ? null : (String) corePath+ed.getUserData("path"); - c.getPieces().add(gen.new Piece(null, "URL: ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(ref, fullUrl, null)); - } - - if (definition.hasSlicing()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold")); - c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); - } - if (definition != null) { - if (definition.hasBinding()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - BindingResolution br = pkp.resolveBinding(definition.getBinding()); - c.getPieces().add(checkForNoChange(definition.getBinding(), gen.new Piece(null, "Binding: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getBinding(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url)? br.url : corePath+br.url, br.display, null))); - if (definition.getBinding().hasStrength()) { - c.getPieces().add(checkForNoChange(definition.getBinding(), gen.new Piece(null, " (", null))); - c.getPieces().add(checkForNoChange(definition.getBinding(), gen.new Piece(corePath+"terminologies.html#"+definition.getBinding().getStrength().toCode(), definition.getBinding().getStrength().toCode(), definition.getBinding().getStrength().getDefinition()))); - c.getPieces().add(gen.new Piece(null, ")", null)); - } - } - for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); - } - if (definition.hasFixed()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, buildJson(definition.getFixed()), null).addStyle("color: darkgreen"))); - } else if (definition.hasPattern()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); - } else if (definition.hasExample()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - c.getPieces().add(checkForNoChange(definition.getExample(), gen.new Piece(null, "Example: ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(definition.getExample(), gen.new Piece(null, buildJson(definition.getExample()), null).addStyle("color: darkgreen"))); - } - } - } - } - return c; - } - - private String buildJson(Type value) throws IOException { - if (value instanceof PrimitiveType) - return ((PrimitiveType) value).asStringValue(); - - IParser json = context.newJsonParser(); - return json.composeString(value, null); - } - - - public String describeSlice(ElementDefinitionSlicingComponent slicing) { - return (slicing.getOrdered() ? "Ordered, " : "Unordered, ")+describe(slicing.getRules())+", by "+commas(slicing.getDiscriminator()); - } - - private String commas(List discriminator) { - CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); - for (StringType id : discriminator) - c.append(id.asStringValue()); - return c.toString(); - } - - - private String describe(SlicingRules rules) { - switch (rules) { - case CLOSED : return "Closed"; - case OPEN : return "Open"; - case OPENATEND : return "Open At End"; - default: - return "??"; - } - } - - private boolean onlyInformationIsMapping(List list, ElementDefinition e) { - return (!e.hasName() && !e.hasSlicing() && (onlyInformationIsMapping(e))) && - getChildren(list, e).isEmpty(); - } - - private boolean onlyInformationIsMapping(ElementDefinition d) { - return !d.hasShort() && !d.hasDefinition() && - !d.hasRequirements() && !d.getAlias().isEmpty() && !d.hasMinElement() && - !d.hasMax() && !d.getType().isEmpty() && !d.hasNameReference() && - !d.hasExample() && !d.hasFixed() && !d.hasMaxLengthElement() && - !d.getCondition().isEmpty() && !d.getConstraint().isEmpty() && !d.hasMustSupportElement() && - !d.hasBinding(); - } - - private boolean allTypesAre(List types, String name) { - for (TypeRefComponent t : types) { - if (!t.getCode().equals(name)) - return false; - } - return true; - } - - private List getChildren(List all, ElementDefinition element) { - List result = new ArrayList(); - int i = all.indexOf(element)+1; - while (i < all.size() && all.get(i).getPath().length() > element.getPath().length()) { - if ((all.get(i).getPath().substring(0, element.getPath().length()+1).equals(element.getPath()+".")) && !all.get(i).getPath().substring(element.getPath().length()+1).contains(".")) - result.add(all.get(i)); - i++; - } - return result; - } - - private String tail(String path) { - if (path.contains(".")) - return path.substring(path.lastIndexOf('.')+1); - else - return path; - } - - private boolean isDataType(String value) { - return Utilities.existsInList(value, "Identifier", "HumanName", "Address", "ContactPoint", "Timing", "SimpleQuantity", "Quantity", "Attachment", "Range", - "Period", "Ratio", "CodeableConcept", "Coding", "SampledData", "Age", "Distance", "Duration", "Count", "Money"); - } - - private boolean isReference(String value) { - return value.equals("Reference"); - } - - public static boolean isPrimitive(String value) { - return value == null || Utilities.existsInListNC(value, "boolean", "integer", "decimal", "base64Binary", "instant", "string", "date", "dateTime", "code", "oid", "uuid", "id", "uri"); - } - -// private static String listStructures(StructureDefinition p) { -// StringBuilder b = new StringBuilder(); -// boolean first = true; -// for (ProfileStructureComponent s : p.getStructure()) { -// if (first) -// first = false; -// else -// b.append(", "); -// if (pkp != null && pkp.hasLinkFor(s.getType())) -// b.append(""+s.getType()+""); -// else -// b.append(s.getType()); -// } -// return b.toString(); -// } - - - public StructureDefinition getProfile(StructureDefinition source, String url) { - StructureDefinition profile; - String code; - if (url.startsWith("#")) { - profile = source; - code = url.substring(1); - } else { - String[] parts = url.split("\\#"); - profile = context.fetchResource(StructureDefinition.class, parts[0]); - code = parts.length == 1 ? null : parts[1]; - } - if (profile == null) - return null; - if (code == null) - return profile; - for (Resource r : profile.getContained()) { - if (r instanceof StructureDefinition && r.getId().equals(code)) - return (StructureDefinition) r; - } - return null; - } - - - - public static class ElementDefinitionHolder { - private String name; - private ElementDefinition self; - private int baseIndex = 0; - private List children; - - public ElementDefinitionHolder(ElementDefinition self) { - super(); - this.self = self; - this.name = self.getPath(); - children = new ArrayList(); - } - - public ElementDefinition getSelf() { - return self; - } - - public List getChildren() { - return children; - } - - public int getBaseIndex() { - return baseIndex; - } - - public void setBaseIndex(int baseIndex) { - this.baseIndex = baseIndex; - } - - } - - public static class ElementDefinitionComparer implements Comparator { - - private boolean inExtension; - private List snapshot; - private int prefixLength; - private String base; - private String name; - private Set errors = new HashSet(); - - public ElementDefinitionComparer(boolean inExtension, List snapshot, String base, int prefixLength, String name) { - this.inExtension = inExtension; - this.snapshot = snapshot; - this.prefixLength = prefixLength; - this.base = base; - this.name = name; - } - - @Override - public int compare(ElementDefinitionHolder o1, ElementDefinitionHolder o2) { - if (o1.getBaseIndex() == 0) - o1.setBaseIndex(find(o1.getSelf().getPath())); - if (o2.getBaseIndex() == 0) - o2.setBaseIndex(find(o2.getSelf().getPath())); - return o1.getBaseIndex() - o2.getBaseIndex(); - } - - private int find(String path) { - String actual = base+path.substring(prefixLength); - for (int i = 0; i < snapshot.size(); i++) { - String p = snapshot.get(i).getPath(); - if (p.equals(actual)) - return i; - if (p.endsWith("[x]") && actual.startsWith(p.substring(0, p.length()-3)) && !(actual.endsWith("[x]")) && !actual.substring(p.length()-3).contains(".")) - return i; - } - if (prefixLength == 0) - errors.add("Differential contains path "+path+" which is not found in the base"); - else - errors.add("Differential contains path "+path+" which is actually "+actual+", which is not found in the base"); - return 0; - } - - public void checkForErrors(List errorList) { - if (errors.size() > 0) { -// CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); -// for (String s : errors) -// b.append("StructureDefinition "+name+": "+s); -// throw new DefinitionException(b.toString()); - for (String s : errors) - if (s.startsWith("!")) - errorList.add("!StructureDefinition "+name+": "+s.substring(1)); - else - errorList.add("StructureDefinition "+name+": "+s); - } - } - } - - - public void sortDifferential(StructureDefinition base, StructureDefinition diff, String name, List errors) { - - final List diffList = diff.getDifferential().getElement(); - // first, we move the differential elements into a tree - ElementDefinitionHolder edh = new ElementDefinitionHolder(diffList.get(0)); - - boolean hasSlicing = false; - List paths = new ArrayList(); // in a differential, slicing may not be stated explicitly - for(ElementDefinition elt : diffList) { - if (elt.hasSlicing() || paths.contains(elt.getPath())) { - hasSlicing = true; - break; - } - paths.add(elt.getPath()); - } - if(!hasSlicing) { - // if Differential does not have slicing then safe to pre-sort the list - // so elements and subcomponents are together - Collections.sort(diffList, new ElementNameCompare()); - } - - int i = 1; - processElementsIntoTree(edh, i, diff.getDifferential().getElement()); - - // now, we sort the siblings throughout the tree - ElementDefinitionComparer cmp = new ElementDefinitionComparer(true, base.getSnapshot().getElement(), "", 0, name); - sortElements(edh, cmp, errors); - - // now, we serialise them back to a list - diffList.clear(); - writeElements(edh, diffList); - } - - private int processElementsIntoTree(ElementDefinitionHolder edh, int i, List list) { - String path = edh.getSelf().getPath(); - final String prefix = path + "."; - while (i < list.size() && list.get(i).getPath().startsWith(prefix)) { - ElementDefinitionHolder child = new ElementDefinitionHolder(list.get(i)); - edh.getChildren().add(child); - i = processElementsIntoTree(child, i+1, list); - } - return i; - } - - private void sortElements(ElementDefinitionHolder edh, ElementDefinitionComparer cmp, List errors) { - if (edh.getChildren().size() == 1) - // special case - sort needsto allocate base numbers, but there'll be no sort if there's only 1 child. So in that case, we just go ahead and allocated base number directly - edh.getChildren().get(0).baseIndex = cmp.find(edh.getChildren().get(0).getSelf().getPath()); - else - Collections.sort(edh.getChildren(), cmp); - cmp.checkForErrors(errors); - - for (ElementDefinitionHolder child : edh.getChildren()) { - if (child.getChildren().size() > 0) { - // what we have to check for here is running off the base profile into a data type profile - ElementDefinition ed = cmp.snapshot.get(child.getBaseIndex()); - ElementDefinitionComparer ccmp; - if (ed.getType().isEmpty() || isAbstract(ed.getType().get(0).getCode()) || ed.getType().get(0).getCode().equals(ed.getPath())) { - ccmp = new ElementDefinitionComparer(true, cmp.snapshot, cmp.base, cmp.prefixLength, cmp.name); - } else if (ed.getType().get(0).getCode().equals("Extension") && child.getSelf().getType().size() == 1 && child.getSelf().getType().get(0).hasProfile()) { - ccmp = new ElementDefinitionComparer(true, context.fetchResource(StructureDefinition.class, child.getSelf().getType().get(0).getProfile().get(0).getValue()).getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name); - } else if (ed.getType().size() == 1 && !ed.getType().get(0).getCode().equals("*")) { - ccmp = new ElementDefinitionComparer(false, context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+ed.getType().get(0).getCode()).getSnapshot().getElement(), ed.getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name); - } else if (child.getSelf().getType().size() == 1) { - ccmp = new ElementDefinitionComparer(false, context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+child.getSelf().getType().get(0).getCode()).getSnapshot().getElement(), child.getSelf().getType().get(0).getCode(), child.getSelf().getPath().length(), cmp.name); - } else if (ed.getPath().endsWith("[x]") && !child.getSelf().getPath().endsWith("[x]")) { - String p = child.getSelf().getPath().substring(ed.getPath().length()-3); - StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+p); - if (sd == null) - throw new Error("Unable to find profile "+p); - ccmp = new ElementDefinitionComparer(false, sd.getSnapshot().getElement(), p, child.getSelf().getPath().length(), cmp.name); - } else { - throw new Error("Not handled yet (sortElements: "+ed.getPath()+":"+typeCode(ed.getType())+")"); - } - sortElements(child, ccmp, errors); - } - } - } - - private boolean isAbstract(String code) { - return code.equals("Element") || code.equals("BackboneElement") || code.equals("Resource") || code.equals("DomainResource"); - } - - - private void writeElements(ElementDefinitionHolder edh, List list) { - list.add(edh.getSelf()); - for (ElementDefinitionHolder child : edh.getChildren()) { - writeElements(child, list); - } - } - - /** - * First compare element by path then by name if same - */ - private static class ElementNameCompare implements Comparator { - - @Override - public int compare(ElementDefinition o1, ElementDefinition o2) { - String path1 = normalizePath(o1); - String path2 = normalizePath(o2); - int cmp = path1.compareTo(path2); - if (cmp == 0) { - String name1 = o1.hasName() ? o1.getName() : ""; - String name2 = o2.hasName() ? o2.getName() : ""; - cmp = name1.compareTo(name2); - } - return cmp; - } - - private static String normalizePath(ElementDefinition e) { - if (!e.hasPath()) return ""; - String path = e.getPath(); - // if sorting element names make sure onset[x] appears before onsetAge, onsetDate, etc. - // so strip off the [x] suffix when comparing the path names. - if (path.endsWith("[x]")) { - path = path.substring(0, path.length()-3); - } - return path; - } - - } - - // generate schematroins for the rules in a structure definition - - public void generateSchematrons(OutputStream dest, StructureDefinition structure) throws IOException, DefinitionException { - if (!structure.hasConstrainedType()) - throw new DefinitionException("not the right kind of structure to generate schematrons for"); - if (!structure.hasSnapshot()) - throw new DefinitionException("needs a snapshot"); - - StructureDefinition base = context.fetchResource(StructureDefinition.class, structure.getBase()); - - SchematronWriter sch = new SchematronWriter(dest, SchematronType.PROFILE, base.getName()); - - ElementDefinition ed = structure.getSnapshot().getElement().get(0); - generateForChildren(sch, "f:"+ed.getPath(), ed, structure, base); - sch.dump(); - } - - private class Slicer extends ElementDefinitionSlicingComponent { - String criteria = ""; - String name = ""; - boolean check; - public Slicer(boolean cantCheck) { - super(); - this.check = cantCheck; - } - } - - private Slicer generateSlicer(ElementDefinition child, ElementDefinitionSlicingComponent slicing, StructureDefinition structure) { - // given a child in a structure, it's sliced. figure out the slicing xpath - if (child.getPath().endsWith(".extension")) { - ElementDefinition ued = getUrlFor(structure, child); - if ((ued == null || !ued.hasFixed()) && !(child.getType().get(0).hasProfile())) - return new Slicer(false); - else { - Slicer s = new Slicer(true); - String url = (ued == null || !ued.hasFixed()) ? child.getType().get(0).getProfile().get(0).asStringValue() : ((UriType) ued.getFixed()).asStringValue(); - s.name = " with URL = '"+url+"'"; - s.criteria = "[@url = '"+url+"']"; - return s; - } - } else - return new Slicer(false); - } - - private void generateForChildren(SchematronWriter sch, String xpath, ElementDefinition ed, StructureDefinition structure, StructureDefinition base) throws IOException { - // generateForChild(txt, structure, child); - List children = getChildList(structure, ed); - String sliceName = null; - ElementDefinitionSlicingComponent slicing = null; - for (ElementDefinition child : children) { - String name = tail(child.getPath()); - if (child.hasSlicing()) { - sliceName = name; - slicing = child.getSlicing(); - } else if (!name.equals(sliceName)) - slicing = null; - - ElementDefinition based = getByPath(base, child.getPath()); - boolean doMin = (child.getMin() > 0) && (based == null || (child.getMin() != based.getMin())); - boolean doMax = !child.getMax().equals("*") && (based == null || (!child.getMax().equals(based.getMax()))); - Slicer slicer = slicing == null ? new Slicer(true) : generateSlicer(child, slicing, structure); - if (slicer.check) { - if (doMin || doMax) { - Section s = sch.section(xpath); - Rule r = s.rule(xpath); - if (doMin) - r.assrt("count(f:"+name+slicer.criteria+") >= "+Integer.toString(child.getMin()), name+slicer.name+": minimum cardinality of '"+name+"' is "+Integer.toString(child.getMin())); - if (doMax) - r.assrt("count(f:"+name+slicer.criteria+") <= "+child.getMax(), name+slicer.name+": maximum cardinality of '"+name+"' is "+child.getMax()); - } - } - } - for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) { - if (inv.hasXpath()) { - Section s = sch.section(ed.getPath()); - Rule r = s.rule(xpath); - r.assrt(inv.getXpath(), (inv.hasId() ? inv.getId()+": " : "")+inv.getHuman()+(inv.hasUserData(IS_DERIVED) ? " (inherited)" : "")); - } - } - for (ElementDefinition child : children) { - String name = tail(child.getPath()); - generateForChildren(sch, xpath+"/f:"+name, child, structure, base); - } - } - - - - - private ElementDefinition getByPath(StructureDefinition base, String path) { - for (ElementDefinition ed : base.getSnapshot().getElement()) { - if (ed.getPath().equals(path)) - return ed; - if (ed.getPath().endsWith("[x]") && ed.getPath().length() <= path.length()-3 && ed.getPath().substring(0, ed.getPath().length()-3).equals(path.substring(0, ed.getPath().length()-3))) - return ed; - } - return null; - } - -// -//private void generateForChild(TextStreamWriter txt, -// StructureDefinition structure, ElementDefinition child) { -// // TODO Auto-generated method stub -// -//} - - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu21Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu21Test.java index 61c6fed96ed..4f47f3f34f3 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu21Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu21Test.java @@ -32,6 +32,7 @@ import org.hamcrest.text.StringContainsInOrder; import org.hl7.fhir.dstu21.model.Address.AddressUse; import org.hl7.fhir.dstu21.model.AllergyIntolerance; import org.hl7.fhir.dstu21.model.Annotation; +import org.hl7.fhir.dstu21.model.Attachment; import org.hl7.fhir.dstu21.model.Binary; import org.hl7.fhir.dstu21.model.Bundle; import org.hl7.fhir.dstu21.model.Bundle.BundleEntryComponent; @@ -82,6 +83,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.Constants; @@ -1679,6 +1681,12 @@ public class XmlParserDstu21Test { DocumentManifest actual = ourCtx.newXmlParser().parseResource(DocumentManifest.class, encoded); assertEquals(1, actual.getContained().size()); assertEquals(1, actual.getContent().size()); + + /* + * If this fails, it's possibe the DocumentManifest structure is wrong: + * It should be + * @Child(name = "p", type = {Attachment.class, ValueSet.class}, order=1, min=1, max=1, modifier=false, summary=true) + */ assertNotNull(((Reference) actual.getContent().get(0).getP()).getResource()); } @@ -1730,7 +1738,7 @@ public class XmlParserDstu21Test { " \n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + @@ -1758,9 +1766,10 @@ public class XmlParserDstu21Test { assertEquals("match", entry.getSearch().getMode().toCode()); assertEquals("POST", entry.getRequest().getMethod().toCode()); assertEquals("http://foo/Patient?identifier=value", entry.getRequest().getUrl()); - assertEquals("2001-02-22T11:22:33-05:00", pt.getMeta().getLastUpdatedElement().getValueAsString()); + assertEquals("2001-02-22T09:22:33-07:00", pt.getMeta().getLastUpdatedElement().getValueAsString()); - String reEncoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b); + IParser p = ourCtx.newXmlParser().setPrettyPrint(true); + String reEncoded = p.encodeResourceToString(b); ourLog.info(reEncoded); Diff d = new Diff(new StringReader(bundle), new StringReader(reEncoded)); diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu21Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu21Test.java index 73831c1bf23..bb4ff5b9f03 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu21Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu21Test.java @@ -11,23 +11,22 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.Search; public class ServerUsingOldTypesDstu21Test { private static FhirContext ourCtx = FhirContext.forDstu2_1(); - + @Test - public void testReadProvider() { + public void testReadProviderString() { RestfulServer srv = new RestfulServer(ourCtx); srv.setFhirContext(ourCtx); - srv.setResourceProviders(new ReadProvider()); + srv.setResourceProviders(new ReadProviderString()); try { srv.init(); @@ -36,6 +35,21 @@ public class ServerUsingOldTypesDstu21Test { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); } } + + @Test + public void testReadProviderIdDt() { + RestfulServer srv = new RestfulServer(ourCtx); + srv.setFhirContext(ourCtx); + srv.setResourceProviders(new ReadProviderIdDt()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + } + } + @Test public void testOperationProvider() { RestfulServer srv = new RestfulServer(ourCtx); @@ -49,8 +63,8 @@ public class ServerUsingOldTypesDstu21Test { assertThat(e.getCause().toString(), StringContains.containsString("Incorrect use of type")); } } - - public static class ReadProvider implements IResourceProvider { + + public static class ReadProviderString implements IResourceProvider { @Override public Class getResourceType() { @@ -64,6 +78,20 @@ public class ServerUsingOldTypesDstu21Test { } + public static class ReadProviderIdDt implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Read + public Patient opTypeRetOldBundle(@IdParam IdDt theId) { + return null; + } + + } + public static class OperationProvider implements IResourceProvider { @Override @@ -71,8 +99,8 @@ public class ServerUsingOldTypesDstu21Test { return Patient.class; } - @Operation(name="foo") - public Patient opTypeRetOldBundle(@OperationParam(name="foo") IntegerDt theId) { + @Operation(name = "foo") + public Patient opTypeRetOldBundle(@OperationParam(name = "foo") IntegerDt theId) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java index 141ae69db7b..4eeb7533235 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java @@ -57,6 +57,7 @@ import org.hl7.fhir.instance.model.Conformance.TypeRestfulInteraction; import org.hl7.fhir.instance.model.Conformance.UnknownContentCode; import org.hl7.fhir.instance.model.Enumerations.ConformanceResourceStatus; import org.hl7.fhir.instance.model.Enumerations.ResourceType; +import org.hl7.fhir.instance.model.IdType; import org.hl7.fhir.instance.model.OperationDefinition; import org.hl7.fhir.instance.model.OperationDefinition.OperationDefinitionParameterComponent; import org.hl7.fhir.instance.model.OperationDefinition.OperationParameterUse; @@ -64,8 +65,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Initialize; import ca.uhn.fhir.rest.annotation.Metadata; @@ -82,7 +81,6 @@ import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.IServerConformanceProvider; import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; /** @@ -512,7 +510,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider ourLastParam3; @@ -294,7 +298,7 @@ public class OperationServerHl7OrgTest { @Test public void testInstanceEverythingHapiClient() throws Exception { - Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdDt("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); Bundle b = (Bundle) p.getParameter().get(0).getResource(); assertNotNull(b); @@ -416,7 +420,7 @@ public class OperationServerHl7OrgTest { * Just to make sure this method doesn't "steal" calls */ @Read - public Patient read(@IdParam IdDt theId) { + public Patient read(@IdParam IdType theId) { ourLastMethod = "read"; Patient retVal = new Patient(); retVal.setId(theId); @@ -424,7 +428,7 @@ public class OperationServerHl7OrgTest { } @Operation(name = "$everything", idempotent=true) - public Bundle patientEverything(@IdParam IdDt thePatientId) { + public Bundle patientEverything(@IdParam IdType thePatientId) { ourLastMethod = "instance $everything"; ourLastId = thePatientId; return new Bundle(); @@ -482,7 +486,7 @@ public class OperationServerHl7OrgTest { //@formatter:off @Operation(name="$OP_INSTANCE") public Parameters opInstance( - @IdParam IdDt theId, + @IdParam IdType theId, @OperationParam(name="PARAM1") StringType theParam1, @OperationParam(name="PARAM2") Patient theParam2 ) { diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java index 2ab8115f74b..feef96aff37 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import java.util.concurrent.TimeUnit; @@ -15,13 +15,13 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.instance.model.IdType; import org.hl7.fhir.instance.model.Patient; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; @@ -103,13 +103,13 @@ public class PreferHl7OrgDstu2Test { @Create() public MethodOutcome createPatient(@ResourceParam Patient thePatient) { - MethodOutcome retVal = new MethodOutcome(new IdDt("Patient/001/_history/002")); + MethodOutcome retVal = new MethodOutcome(new IdType("Patient/001/_history/002")); return retVal; } @Update() - public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @IdParam IdDt theIdParam) { - return new MethodOutcome(new IdDt("Patient/001/_history/002")); + public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @IdParam IdType theIdParam) { + return new MethodOutcome(new IdType("Patient/001/_history/002")); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java index c2b4a5a615e..38e8990ed62 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java @@ -1,8 +1,13 @@ package ca.uhn.fhir.rest.server; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.Collection; import java.util.List; @@ -18,18 +23,18 @@ import org.hl7.fhir.instance.model.Conformance.ConformanceRestComponent; import org.hl7.fhir.instance.model.Conformance.ConformanceRestResourceComponent; import org.hl7.fhir.instance.model.Conformance.SystemRestfulInteraction; import org.hl7.fhir.instance.model.Conformance.TypeRestfulInteraction; +import org.hl7.fhir.instance.model.DateType; import org.hl7.fhir.instance.model.DiagnosticReport; +import org.hl7.fhir.instance.model.IdType; import org.hl7.fhir.instance.model.OperationDefinition; import org.hl7.fhir.instance.model.Patient; +import org.hl7.fhir.instance.model.StringType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.primitive.DateDt; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Delete; @@ -49,6 +54,7 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.SearchMethodBinding; import ca.uhn.fhir.rest.method.SearchParameter; import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -128,7 +134,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { rs.init(createServletConfig()); - OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/everything")); + OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/everything")); String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef); ourLog.info(conf); @@ -263,7 +269,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { Conformance sconf = sc.getServerConformance(createHttpServletRequest()); assertEquals("OperationDefinition/plain", sconf.getRest().get(0).getOperation().get(0).getDefinition().getReference()); - OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/plain")); + OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/plain")); String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef); ourLog.info(conf); @@ -448,7 +454,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Delete - public MethodOutcome delete(@IdParam IdDt theId, @ConditionalUrlParam String theConditionalUrl) { + public MethodOutcome delete(@IdParam IdType theId, @ConditionalUrlParam String theConditionalUrl) { return null; } @@ -458,7 +464,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Update - public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { + public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { return null; } @@ -471,7 +477,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @History - public List history(@IdParam IdDt theId) { + public List history(@IdParam IdType theId) { return null; } @@ -483,7 +489,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { public static class MultiOptionalProvider { @Search(type = Patient.class) - public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier, @Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringDt theName) { + public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier, @Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) { return null; } @@ -497,7 +503,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Delete - public MethodOutcome delete(@IdParam IdDt theId) { + public MethodOutcome delete(@IdParam IdType theId) { return null; } @@ -507,7 +513,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Update - public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient thePatient) { + public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient) { return null; } @@ -516,9 +522,9 @@ public class ServerConformanceProviderHl7OrgDstu2Test { public static class PlainProviderWithExtendedOperationOnNoType { @Operation(name = "plain", idempotent = true, returnParameters= { - @OperationParam(min=1, max=2, name="out1", type=StringDt.class) + @OperationParam(min=1, max=2, name="out1", type=StringType.class) }) - public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -527,7 +533,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public ca.uhn.fhir.rest.server.IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -560,7 +566,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Read(version = false) - public Patient readPatient(@IdParam IdDt theId) { + public Patient readPatient(@IdParam IdType theId) { return null; } @@ -612,7 +618,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { } @Read(version = true) - public Patient readPatient(@IdParam IdDt theId) { + public Patient readPatient(@IdParam IdType theId) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionHl7OrgDstu2Test.java index a6824773b08..e80e9e9b9ec 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionHl7OrgDstu2Test.java @@ -1,50 +1,56 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import javax.servlet.ServletException; import org.hamcrest.core.StringContains; import org.hl7.fhir.instance.model.Patient; +import org.hl7.fhir.instance.model.StringType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; public class ServerInvalidDefinitionHl7OrgDstu2Test { - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - @Test - public void testOperationReturningOldBundleProvider() { - RestfulServer srv = new RestfulServer(ourCtx); - srv.setFhirContext(ourCtx); - srv.setResourceProviders(new OperationReturningOldBundleProvider()); + private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - try { - srv.init(); - fail(); - } catch (ServletException e) { - assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); - assertThat(e.getCause().toString(), StringContains.containsString("Can not return a DSTU1 bundle")); - } - } + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory + .getLogger(ServerInvalidDefinitionHl7OrgDstu2Test.class); - public static class OperationReturningOldBundleProvider implements IResourceProvider { + @Test + public void testOperationReturningOldBundleProvider() { + RestfulServer srv = new RestfulServer(ourCtx); + srv.setFhirContext(ourCtx); + srv.setResourceProviders(new OperationReturningOldBundleProvider()); - @Override - public Class getResourceType() { - return Patient.class; - } + try { + srv.init(); + fail(); + } catch (ServletException e) { + ourLog.info(e.getCause().toString()); + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + assertThat(e.getCause().toString(), StringContains.containsString("Can not return a DSTU1 bundle")); + } + } - @Operation(name = "$OP_TYPE_RET_OLD_BUNDLE") - public ca.uhn.fhir.model.api.Bundle opTypeRetOldBundle(@OperationParam(name = "PARAM1") StringDt theParam1, @OperationParam(name = "PARAM2") Patient theParam2) { - return null; - } + public static class OperationReturningOldBundleProvider implements IResourceProvider { - } + @Override + public Class getResourceType() { + return Patient.class; + } + + @Operation(name = "$OP_TYPE_RET_OLD_BUNDLE") + public ca.uhn.fhir.model.api.Bundle opTypeRetOldBundle(@OperationParam(name = "PARAM1") StringType theParam1, + @OperationParam(name = "PARAM2") Patient theParam2) { + return null; + } + + } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java index 3b078fbc8ff..df456db3869 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java @@ -1,6 +1,8 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; @@ -27,7 +29,6 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.IdParam; @@ -49,7 +50,7 @@ public class UpdateConditionalHl7OrgDstu2Test { private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); private static Server ourServer; private static String ourLastId; - private static IdDt ourLastIdParam; + private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; @@ -179,11 +180,11 @@ public class UpdateConditionalHl7OrgDstu2Test { } @Update() - public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdDt theIdParam) { + public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) { ourLastConditionalUrl = theConditional; ourLastId = thePatient.getId(); ourLastIdParam = theIdParam; - return new MethodOutcome(new IdDt("Patient/001/_history/002")); + return new MethodOutcome(new IdType("Patient/001/_history/002")); } } diff --git a/sync_ri21.sh b/sync_ri21.sh index 965c72e7ed2..ee6db6b3a71 100755 --- a/sync_ri21.sh +++ b/sync_ri21.sh @@ -80,7 +80,6 @@ cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl7/fhir/$PACKAGEVERSION/utils/INarrativeGenerator.java hapi-fhir-structures-$PROJVERSION/src/main/java/org/hl7/fhir/$PACKAGEVERSION/utils/ cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl7/fhir/$PACKAGEVERSION/utils/EOperationOutcome.java hapi-fhir-structures-$PROJVERSION/src/main/java/org/hl7/fhir/$PACKAGEVERSION/utils/ cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl7/fhir/$PACKAGEVERSION/utils/FHIRPathEngine.java hapi-fhir-structures-$PROJVERSION/src/main/java/org/hl7/fhir/$PACKAGEVERSION/utils/ -cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl7/fhir/$PACKAGEVERSION/utils/ProfileUtilities.java hapi-fhir-structures-$PROJVERSION/src/main/java/org/hl7/fhir/$PACKAGEVERSION/utils/ # Validation cp $FHIRTRUNK/build/implementations/java/org.hl7.fhir.$PACKAGEVERSION/src/org/hl7/fhir/$PACKAGEVERSION/validation/* hapi-fhir-structures-$PROJVERSION/src/main/java/org/hl7/fhir/$PACKAGEVERSION/validation/