diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..2ec819d6f47 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true + +[*.java] +charset = utf-8 +indent_style = tab diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 99f26c0203a..00000000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 8000cd6ca61..00000000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1cb2..00000000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index e9441bb123e..00000000000 --- a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding/=UTF-8 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1cb2..00000000000 --- a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml index ce746e8ae97..c6e01302dab 100644 --- a/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml +++ b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir 2.3-SNAPSHOT - ../hapi-deployable-pom/pom.xml + ../../pom.xml jar @@ -47,7 +47,7 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 2.1-SNAPSHOT + ${project.version} org.slf4j diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index f9fe34593fc..00000000000 --- a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 -encoding/=UTF-8 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 714351aec19..00000000000 --- a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1cb2..00000000000 --- a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/pom.xml b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml index fef33f1705c..2cf09371dd2 100644 --- a/example-projects/hapi-fhir-standalone-overlay-example/pom.xml +++ b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir 2.3-SNAPSHOT - ../hapi-deployable-pom/pom.xml + ../../pom.xml hapi-fhir-standalone-overlay-example @@ -37,14 +37,14 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 2.3-SNAPSHOT + ${project.version} war provided ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 2.3-SNAPSHOT + ${project.version} classes provided diff --git a/examples/src/main/java/example/ClientTransactionExamples.java b/examples/src/main/java/example/ClientTransactionExamples.java index 790008bc66c..f7c3aff790b 100644 --- a/examples/src/main/java/example/ClientTransactionExamples.java +++ b/examples/src/main/java/example/ClientTransactionExamples.java @@ -70,7 +70,7 @@ public class ClientTransactionExamples { .setResource(patient) .getRequest() .setUrl("Patient") - .setIfNoneExist("Patient?identifier=http://acme.org/mrns|12345") + .setIfNoneExist("identifier=http://acme.org/mrns|12345") .setMethod(HTTPVerbEnum.POST); // Add the observation. This entry is a POST with no header diff --git a/examples/src/main/java/example/PagingPatientProvider.java b/examples/src/main/java/example/PagingPatientProvider.java index ab398a3679a..ce70e046e76 100644 --- a/examples/src/main/java/example/PagingPatientProvider.java +++ b/examples/src/main/java/example/PagingPatientProvider.java @@ -60,6 +60,11 @@ public class PagingPatientProvider implements IResourceProvider { // Typically this method just returns null return null; } + + @Override + public String getUuid() { + return null; + } }; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java index 2ec9745efaf..1bf3cec2f60 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java @@ -474,9 +474,8 @@ public abstract class BaseRuntimeElementCompositeDefinition ext for (String nextName : next.getValidChildNames()) { if (myNameToChild.containsKey(nextName)) { throw new ConfigurationException("Duplicate child name[" + nextName + "] in Element[" + getName() + "]"); - } else { - myNameToChild.put(nextName, next); } + myNameToChild.put(nextName, next); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index e4107a028c0..3121a8ad1ae 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -148,10 +148,10 @@ public abstract class BaseRuntimeElementDefinition { public T newInstance(Object theArgument) { try { if (theArgument == null) { - return getConstructor(null).newInstance(null); - } else { - return getConstructor(theArgument).newInstance(theArgument); + return getConstructor(null).newInstance(); } + return getConstructor(theArgument).newInstance(theArgument); + } catch (Exception e) { throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); } @@ -171,9 +171,8 @@ public abstract class BaseRuntimeElementDefinition { String extUrl = next.getExtensionUrl(); if (myUrlToExtension.containsKey(extUrl)) { throw new ConfigurationException("Duplicate extension URL[" + extUrl + "] in Element[" + getName() + "]"); - } else { - myUrlToExtension.put(extUrl, next); } + myUrlToExtension.put(extUrl, next); if (next.isModifier()) { myExtensionsModifier.add(next); } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java index 44c35d99ca4..ce4e4b41ef7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java @@ -1,7 +1,5 @@ package ca.uhn.fhir.context; -import java.util.concurrent.Callable; - /* * #%L * HAPI FHIR - Core Library diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index e1d96461213..ac6015635e3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -244,7 +244,9 @@ class ModelScanner { } } - if (blockDefinition == null && datatypeDefinition == null && resourceDefinition == null) { + if (blockDefinition == null +//Redundant checking && datatypeDefinition == null && resourceDefinition == null + ) { throw new ConfigurationException("Resource class[" + theClass.getName() + "] does not contain any valid HAPI-FHIR annotations"); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java index 5012deba3db..706368937bb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java @@ -114,9 +114,9 @@ public class RuntimeChildDeclaredExtensionDefinition extends RuntimeChildChoiceD if (retVal == null) { if (myModifier) { return "modifierExtension"; - } else { - return "extension"; } + return "extension"; + } return retVal; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDirectResource.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDirectResource.java index 52829f7067a..35dc42b206d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDirectResource.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDirectResource.java @@ -33,7 +33,7 @@ import ca.uhn.fhir.model.api.annotation.Description; public class RuntimeChildDirectResource extends BaseRuntimeDeclaredChildDefinition { - private RuntimeElemContainedResources myElem; +// private RuntimeElemContainedResources myElem; private FhirContext myContext; RuntimeChildDirectResource(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtension.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtension.java index cf3dc0145ce..044c661e632 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtension.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildExtension.java @@ -31,7 +31,7 @@ import ca.uhn.fhir.model.api.annotation.Description; public class RuntimeChildExtension extends RuntimeChildAny { - private RuntimeChildUndeclaredExtensionDefinition myExtensionElement; +// private RuntimeChildUndeclaredExtensionDefinition myExtensionElement; public RuntimeChildExtension(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation) { super(theField, theElementName, theChildAnnotation, theDescriptionAnnotation); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceBlockDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceBlockDefinition.java index 81f2835a914..e622d876613 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceBlockDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceBlockDefinition.java @@ -32,7 +32,7 @@ import ca.uhn.fhir.model.api.annotation.Description; public class RuntimeChildResourceBlockDefinition extends BaseRuntimeDeclaredChildDefinition { - private RuntimeResourceBlockDefinition myElementDef; +// private RuntimeResourceBlockDefinition myElementDef; private Class myResourceBlockType; private FhirContext myContext; @@ -46,9 +46,8 @@ public class RuntimeChildResourceBlockDefinition extends BaseRuntimeDeclaredChil public RuntimeResourceBlockDefinition getChildByName(String theName) { if (getElementName().equals(theName)) { return getDefinition(); - }else { - return null; } + return null; } private RuntimeResourceBlockDefinition getDefinition() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java index f22dca3a832..d6f89a0ec29 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java @@ -21,21 +21,16 @@ package ca.uhn.fhir.context; */ import static org.apache.commons.lang3.StringUtils.isBlank; -import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; import java.util.Map; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import ca.uhn.fhir.model.api.BasePrimitive; -import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.ResourceDef; -import ca.uhn.fhir.util.CoverageIgnore; public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition> implements IRuntimeDatatypeDefinition { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java index 28c7377674a..a6109c8ae8b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java @@ -1,10 +1,8 @@ package ca.uhn.fhir.context; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.StringTokenizer; +import static org.apache.commons.lang3.StringUtils.trim; + +import java.util.*; import org.hl7.fhir.instance.model.api.IIdType; @@ -32,6 +30,7 @@ import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; public class RuntimeSearchParam { private final IIdType myId; + private final Set myBase; private final List myCompositeOf; private final String myDescription; private final String myName; @@ -71,6 +70,17 @@ public class RuntimeSearchParam { } else { myTargets = null; } + + HashSet base = new HashSet(); + int indexOf = thePath.indexOf('.'); + if (indexOf != -1) { + base.add(trim(thePath.substring(0, indexOf))); + } + myBase = Collections.unmodifiableSet(base); + } + + public Set getBase() { + return myBase; } public Set getTargets() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/HapiLocalizer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/HapiLocalizer.java index d21169083a5..6646f8fe34b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/HapiLocalizer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/HapiLocalizer.java @@ -93,13 +93,11 @@ public class HapiLocalizer { format = new MessageFormat(formatString.trim()); myKeyToMessageFormat.put(theQualifiedKey, format); return format.format(theParameters).toString(); - } else { - String retVal = findFormatString(theQualifiedKey); - return retVal; } + String retVal = findFormatString(theQualifiedKey); + return retVal; } - public Set getAllKeys(){ HashSet retVal = new HashSet(); for (ResourceBundle nextBundle : myBundle) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseBundle.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseBundle.java index ed101c996d4..568bf15758d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseBundle.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseBundle.java @@ -24,8 +24,9 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.util.ElementUtil; -public class BaseBundle extends BaseElement implements IElement { +public class BaseBundle extends BaseElement /*implements IElement*/ { + private static final long serialVersionUID = 3349586533271409727L; private StringDt myAuthorName; private StringDt myAuthorUri; private IdDt myId; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java index 89c7fc52082..301ec430c03 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java @@ -30,8 +30,9 @@ import org.hl7.fhir.instance.model.api.IBaseDatatype; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; -public abstract class BaseElement implements IElement, ISupportsUndeclaredExtensions { +public abstract class BaseElement implements /*IElement, */ISupportsUndeclaredExtensions { + private static final long serialVersionUID = -3092659584634499332L; private List myFormatCommentsPost; private List myFormatCommentsPre; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseIdentifiableElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseIdentifiableElement.java index d9aa36cec83..e32cb76248b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseIdentifiableElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseIdentifiableElement.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.util.CoverageIgnore; public abstract class BaseIdentifiableElement extends BaseElement implements IIdentifiableElement { + private static final long serialVersionUID = -7816838417076777914L; private String myElementSpecificId; @Override @@ -43,9 +44,8 @@ public abstract class BaseIdentifiableElement extends BaseElement implements IId public IdDt getId() { if (myElementSpecificId == null) { return new LockedId(); - } else { - return new LockedId(myElementSpecificId); } + return new LockedId(myElementSpecificId); } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BasePrimitive.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BasePrimitive.java index b2ddacc0060..03bdd8541dc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BasePrimitive.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BasePrimitive.java @@ -27,7 +27,6 @@ import java.io.ObjectOutput; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.parser.DataFormatException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java index b0116a4422a..0f4de315e06 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java @@ -30,7 +30,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hl7.fhir.instance.model.api.IBase; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; @@ -47,8 +46,9 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.UrlUtil; -public class Bundle extends BaseBundle implements IBase /* implements IElement */{ +public class Bundle extends BaseBundle /* implements IBase implements IElement */{ + private static final long serialVersionUID = 5811989173275366745L; private ResourceMetadataMap myResourceMetadata; private BoundCodeDt myType; private StringDt myBundleId; @@ -127,6 +127,7 @@ public class Bundle extends BaseBundle implements IBase /* implements IElement * if (theResource.getId().isAbsolute()) { entry.getLinkSelf().setValue(theResource.getId().getValue()); + //TODO: Use of a deprecated method should be resolved. entry.getId().setValue(theResource.getId().toVersionless().getValue()); } else if (StringUtils.isNotBlank(theResource.getId().getValue())) { @@ -141,6 +142,7 @@ public class Bundle extends BaseBundle implements IBase /* implements IElement * String resId = theResource.getId().getIdPart(); b.append(resId); + //TODO: Use of a deprecated method should be resolved. entry.getId().setValue(b.toString()); if (isNotBlank(theResource.getId().getVersionIdPart())) { @@ -149,6 +151,7 @@ public class Bundle extends BaseBundle implements IBase /* implements IElement * b.append('/'); b.append(theResource.getId().getVersionIdPart()); } else { + //TODO: Use of a deprecated method should be resolved. IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(theResource); if (versionId != null) { b.append('/'); @@ -176,6 +179,7 @@ public class Bundle extends BaseBundle implements IBase /* implements IElement * InstantDt updated = ResourceMetadataKeyEnum.UPDATED.get(theResource); if (updated != null) { + //TODO: Use of a deprecated method should be resolved. entry.setUpdated(updated); } @@ -323,6 +327,7 @@ public class Bundle extends BaseBundle implements IBase /* implements IElement * if (map == null) { map = new HashMap(); for (BundleEntry next : this.getEntries()) { + //TODO: Use of a deprecated method should be resolved. if (next.getId().isEmpty() == false) { map.put(next.getId().toUnqualified(), next.getResource()); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java index 41bb2a6fbf6..572a372648e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.util.ElementUtil; public class BundleEntry extends BaseBundle { + private static final long serialVersionUID = 2635639739195593736L; //@formatter:off /* **************************************************** * NB: add any new fields to the isEmpty() method!!! diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java index 18dede5ab8b..ca83d6ab10f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java @@ -36,6 +36,8 @@ import ca.uhn.fhir.model.primitive.StringDt; @DatatypeDef(name = "Extension") public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDatatype, IBaseExtension { + private static final long serialVersionUID = 6399491332783085935L; + private boolean myModifier; @Child(name="url", type=StringDt.class, order=0, min=1, max=1) @@ -75,6 +77,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa * since the URL itself can not contain extensions and it was therefore misleading. *

*/ + @Override public String getUrl() { return myUrl != null ? myUrl.getValue() : null; } @@ -95,6 +98,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa * {@link #getUndeclaredModifierExtensions()} to retrieve the child extensions. *

*/ + @Override public IBaseDatatype getValue() { return myValue; } @@ -148,6 +152,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa } @Override + @Deprecated //override deprecated method public List getAllPopulatedChildElementsOfType(Class theType) { return new ArrayList(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IResource.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IResource.java index ba9f34840aa..3b4f6d923d8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IResource.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IResource.java @@ -71,6 +71,7 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode * * @since 1.5 */ + @Override IBaseMetaType getMeta(); /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Include.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Include.java index 2529f16a147..f1fa8ea571c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Include.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Include.java @@ -142,9 +142,8 @@ public class Include { int secondColon = myValue.indexOf(':', firstColon + 1); if (secondColon != -1) { return myValue.substring(firstColon + 1, secondColon); - } else { - return myValue.substring(firstColon + 1); } + return myValue.substring(firstColon + 1); } /** @@ -158,9 +157,8 @@ public class Include { int secondColon = myValue.indexOf(':', firstColon + 1); if (secondColon != -1) { return myValue.substring(secondColon + 1); - } else { - return null; } + return null; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 671af0b64f8..603ef37151f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -307,21 +307,19 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { Object obj = resource.getResourceMetadata().get(SECURITY_LABELS); if (obj == null) { return null; - } else { - try { - @SuppressWarnings("unchecked") - List securityLabels = (List) obj; - if (securityLabels.isEmpty()) - return null; - else - return securityLabels; - } catch (ClassCastException e) { - throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected " - + BaseCodingDt.class.getCanonicalName()); - - } - } + try { + @SuppressWarnings("unchecked") + List securityLabels = (List) obj; + if (securityLabels.isEmpty()) { + return null; + } + return securityLabels; + } catch (ClassCastException e) { + throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected " + + BaseCodingDt.class.getCanonicalName()); + } + } @Override @@ -349,9 +347,8 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof TagList) { if (((TagList) retValObj).isEmpty()) { return null; - } else { - return (TagList) retValObj; } + return (TagList) retValObj; } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected " + TagList.class.getCanonicalName()); @@ -496,9 +493,8 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof DecimalDt) { if (((DecimalDt) retValObj).isEmpty()) { return null; - } else { - return (DecimalDt) retValObj; } + return (DecimalDt) retValObj; } else if (retValObj instanceof String) { if (StringUtils.isBlank((String) retValObj)) { return null; @@ -562,9 +558,8 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof InstantDt) { if (((InstantDt) retValObj).isEmpty()) { return null; - } else { - return (InstantDt) retValObj; - } + } + return (InstantDt) retValObj; } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName()); @@ -577,9 +572,8 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof String) { if (StringUtils.isBlank(((String) retValObj))) { return null; - } else { - return (String) retValObj; - } + } + return (String) retValObj; } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + String.class.getCanonicalName()); @@ -591,15 +585,13 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof String) { if (isNotBlank((String) retValObj)) { return new IdDt((String) retValObj); - } else { - return null; } + return null; } else if (retValObj instanceof IdDt) { if (((IdDt) retValObj).isEmpty()) { return null; - } else { - return (IdDt) retValObj; } + return (IdDt) retValObj; } else if (retValObj instanceof Number) { return new IdDt(((Number) retValObj).toString()); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/ProvidesResources.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/ProvidesResources.java index 13b82d2dd34..ff3e770271e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/ProvidesResources.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/ProvidesResources.java @@ -39,5 +39,5 @@ import java.lang.annotation.RetentionPolicy; */ @Retention(RetentionPolicy.RUNTIME) public @interface ProvidesResources { - Class[] resources(); + Class[] resources(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseCodingDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseCodingDt.java index c86e1b53219..fd3821eb44c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseCodingDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseCodingDt.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.rest.param.TokenParam; public abstract class BaseCodingDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { + private static final long serialVersionUID = 4425182816398730643L; + /** * Gets the value(s) for code (Symbol in syntax defined by the system). creating it if it does not exist. Will not return null. * @@ -85,9 +87,8 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC public String getValueAsQueryToken(FhirContext theContext) { if (getSystemElement().getValueAsString() != null) { return ParameterUtil.escape(StringUtils.defaultString(getSystemElement().getValueAsString())) + '|' + ParameterUtil.escape(getCodeElement().getValueAsString()); - } else { - return ParameterUtil.escape(getCodeElement().getValueAsString()); - } + } + return ParameterUtil.escape(getCodeElement().getValueAsString()); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java index aaf206abf65..c25a128eb60 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.util.DatatypeUtil; public abstract class BaseHumanNameDt extends BaseIdentifiableElement { + private static final long serialVersionUID = 2765500013165698259L; + /** * Gets the value(s) for family (Family name (often called 'Surname')). creating it if it does not exist. Will not return null. * @@ -146,9 +148,8 @@ public abstract class BaseHumanNameDt extends BaseIdentifiableElement { nameParts.addAll(getSuffix()); if (nameParts.size() > 0) { return ca.uhn.fhir.util.DatatypeUtil.joinStringsSpaceSeparated(nameParts); - } else { - return getTextElement().getValue(); } + return getTextElement().getValue(); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseIdentifierDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseIdentifierDt.java index 97bb7dad22f..64474417a5d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseIdentifierDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseIdentifierDt.java @@ -33,6 +33,8 @@ import ca.uhn.fhir.rest.param.StringParam; public abstract class BaseIdentifierDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { + private static final long serialVersionUID = 4400972469749953077L; + @Override public String getQueryParameterQualifier() { return null; @@ -61,13 +63,12 @@ public abstract class BaseIdentifierDt extends BaseIdentifiableElement implement */ @Override public String getValueAsQueryToken(FhirContext theContext) { - UriDt system = (UriDt) getSystemElement(); - StringDt value = (StringDt) getValueElement(); - if (system.getValueAsString() != null) { - return ParameterUtil.escape(StringUtils.defaultString(system.getValueAsString())) + '|' + ParameterUtil.escape(value.getValueAsString()); - } else { - return ParameterUtil.escape(value.getValueAsString()); - } + UriDt system = getSystemElement(); + StringDt value = getValueElement(); + if (system.getValueAsString() != null) { + return ParameterUtil.escape(StringUtils.defaultString(system.getValueAsString())) + '|' + ParameterUtil.escape(value.getValueAsString()); + } + return ParameterUtil.escape(value.getValueAsString()); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseNarrativeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseNarrativeDt.java index c0b756d89f5..10de03d0d76 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseNarrativeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseNarrativeDt.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.model.primitive.XhtmlDt; */ public abstract class BaseNarrativeDt> extends BaseIdentifiableElement implements ICompositeDatatype, INarrative { + private static final long serialVersionUID = -525238683230100077L; + public abstract BoundCodeDt getStatus(); @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseQuantityDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseQuantityDt.java index ef12d27fa14..e8490c195ce 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseQuantityDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseQuantityDt.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.BaseIdentifiableElement; import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.IQueryParameterType; +//TODO: Use of a deprecated method should be resolved. import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.primitive.BoundCodeDt; import ca.uhn.fhir.model.primitive.CodeDt; @@ -38,7 +39,8 @@ import ca.uhn.fhir.rest.param.QuantityParam; public abstract class BaseQuantityDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { - + private static final long serialVersionUID = -925486613033086056L; + /** * Sets the value(s) for value (Numerical value (with implicit precision)) * @@ -64,15 +66,19 @@ public abstract class BaseQuantityDt extends BaseIdentifiableElement implements String[] parts = theValue.split("\\|"); if (parts.length > 0 && StringUtils.isNotBlank(parts[0])) { if (parts[0].startsWith("<=")) { + //TODO: Use of a deprecated method should be resolved. getComparatorElement().setValue(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS.getCode()); setValue(new BigDecimal(parts[0].substring(2))); } else if (parts[0].startsWith("<")) { + //TODO: Use of a deprecated method should be resolved. getComparatorElement().setValue(QuantityCompararatorEnum.LESSTHAN.getCode()); setValue(new BigDecimal(parts[0].substring(1))); } else if (parts[0].startsWith(">=")) { + //TODO: Use of a deprecated method should be resolved. getComparatorElement().setValue(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS.getCode()); setValue(new BigDecimal(parts[0].substring(2))); } else if (parts[0].startsWith(">")) { + //TODO: Use of a deprecated method should be resolved. getComparatorElement().setValue(QuantityCompararatorEnum.GREATERTHAN.getCode()); setValue(new BigDecimal(parts[0].substring(1))); } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/BaseOperationOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/BaseOperationOutcome.java index 83db641b8a5..95cc6d25611 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/BaseOperationOutcome.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/BaseOperationOutcome.java @@ -40,6 +40,8 @@ public interface BaseOperationOutcome extends IResource, IBaseOperationOutcome { public static abstract class BaseIssue extends BaseIdentifiableElement implements IResourceBlock { + private static final long serialVersionUID = 6700020892151450738L; + public abstract CodeDt getSeverityElement(); public abstract StringDt getDetailsElement(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/QuantityCompararatorEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/QuantityCompararatorEnum.java index 1e6adcadacc..3f57e0ba73c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/QuantityCompararatorEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/valueset/QuantityCompararatorEnum.java @@ -119,6 +119,7 @@ public enum QuantityCompararatorEnum { /** * Converts codes to their respective enumerated values */ + @SuppressWarnings("serial") public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { @Override public String toCodeString(QuantityCompararatorEnum theEnum) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java index b0ba33a2112..f579baccf1b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java @@ -98,65 +98,64 @@ public abstract class BaseDateTimeDt extends BasePrimitive { protected String encode(Date theValue) { if (theValue == null) { return null; + } + GregorianCalendar cal; + if (myTimeZoneZulu) { + cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); + } else if (myTimeZone != null) { + cal = new GregorianCalendar(myTimeZone); } else { - GregorianCalendar cal; - if (myTimeZoneZulu) { - cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); - } else if (myTimeZone != null) { - cal = new GregorianCalendar(myTimeZone); - } else { - cal = new GregorianCalendar(); - } - cal.setTime(theValue); + cal = new GregorianCalendar(); + } + cal.setTime(theValue); - StringBuilder b = new StringBuilder(); - leftPadWithZeros(cal.get(Calendar.YEAR), 4, b); - if (myPrecision.ordinal() > TemporalPrecisionEnum.YEAR.ordinal()) { + StringBuilder b = new StringBuilder(); + leftPadWithZeros(cal.get(Calendar.YEAR), 4, b); + if (myPrecision.ordinal() > TemporalPrecisionEnum.YEAR.ordinal()) { + b.append('-'); + leftPadWithZeros(cal.get(Calendar.MONTH) + 1, 2, b); + if (myPrecision.ordinal() > TemporalPrecisionEnum.MONTH.ordinal()) { b.append('-'); - leftPadWithZeros(cal.get(Calendar.MONTH) + 1, 2, b); - if (myPrecision.ordinal() > TemporalPrecisionEnum.MONTH.ordinal()) { - b.append('-'); - leftPadWithZeros(cal.get(Calendar.DATE), 2, b); - if (myPrecision.ordinal() > TemporalPrecisionEnum.DAY.ordinal()) { - b.append('T'); - leftPadWithZeros(cal.get(Calendar.HOUR_OF_DAY), 2, b); + leftPadWithZeros(cal.get(Calendar.DATE), 2, b); + if (myPrecision.ordinal() > TemporalPrecisionEnum.DAY.ordinal()) { + b.append('T'); + leftPadWithZeros(cal.get(Calendar.HOUR_OF_DAY), 2, b); + b.append(':'); + leftPadWithZeros(cal.get(Calendar.MINUTE), 2, b); + if (myPrecision.ordinal() > TemporalPrecisionEnum.MINUTE.ordinal()) { b.append(':'); - leftPadWithZeros(cal.get(Calendar.MINUTE), 2, b); - if (myPrecision.ordinal() > TemporalPrecisionEnum.MINUTE.ordinal()) { - b.append(':'); - leftPadWithZeros(cal.get(Calendar.SECOND), 2, b); - if (myPrecision.ordinal() > TemporalPrecisionEnum.SECOND.ordinal()) { - b.append('.'); - b.append(myFractionalSeconds); - for (int i = myFractionalSeconds.length(); i < 3; i++) { - b.append('0'); - } + leftPadWithZeros(cal.get(Calendar.SECOND), 2, b); + if (myPrecision.ordinal() > TemporalPrecisionEnum.SECOND.ordinal()) { + b.append('.'); + b.append(myFractionalSeconds); + for (int i = myFractionalSeconds.length(); i < 3; i++) { + b.append('0'); } } + } - if (myTimeZoneZulu) { - b.append('Z'); - } else if (myTimeZone != null) { - int offset = myTimeZone.getOffset(theValue.getTime()); - if (offset >= 0) { - b.append('+'); - } else { - b.append('-'); - offset = Math.abs(offset); - } - - int hoursOffset = (int) (offset / DateUtils.MILLIS_PER_HOUR); - leftPadWithZeros(hoursOffset, 2, b); - b.append(':'); - int minutesOffset = (int) (offset % DateUtils.MILLIS_PER_HOUR); - minutesOffset = (int) (minutesOffset / DateUtils.MILLIS_PER_MINUTE); - leftPadWithZeros(minutesOffset, 2, b); + if (myTimeZoneZulu) { + b.append('Z'); + } else if (myTimeZone != null) { + int offset = myTimeZone.getOffset(theValue.getTime()); + if (offset >= 0) { + b.append('+'); + } else { + b.append('-'); + offset = Math.abs(offset); } + + int hoursOffset = (int) (offset / DateUtils.MILLIS_PER_HOUR); + leftPadWithZeros(hoursOffset, 2, b); + b.append(':'); + int minutesOffset = (int) (offset % DateUtils.MILLIS_PER_HOUR); + minutesOffset = (int) (minutesOffset / DateUtils.MILLIS_PER_MINUTE); + leftPadWithZeros(minutesOffset, 2, b); } } } - return b.toString(); } + return b.toString(); } /** @@ -671,7 +670,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive { public BaseDateTimeDt setNanos(long theNanos) { validateValueInRange(theNanos, 0, NANOS_PER_SECOND-1); String fractionalSeconds = StringUtils.leftPad(Long.toString(theNanos), 9, '0'); - + // Strip trailing 0s for (int i = fractionalSeconds.length(); i > 0; i--) { if (fractionalSeconds.charAt(i-1) != '0') { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BooleanDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BooleanDt.java index d3112bd417d..751c43077fc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BooleanDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BooleanDt.java @@ -60,9 +60,8 @@ public class BooleanDt extends BasePrimitive implements IBaseBooleanDat protected String encode(Boolean theValue) { if (Boolean.TRUE.equals(theValue)) { return "true"; - } else { - return "false"; } + return "false"; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java index 6c888f6b92e..008b7fb3e0d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java @@ -34,7 +34,6 @@ import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.SimpleSetter; @@ -54,7 +53,7 @@ import ca.uhn.fhir.util.UrlUtil; *

*/ @DatatypeDef(name = "id", profileOf = StringDt.class) -public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { +public class IdDt extends UriDt implements /*IPrimitiveDatatype, */IIdType { private String myBaseUrl; private boolean myHaveComponentParts; @@ -183,6 +182,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { setValue(theUrl.getValueAsString()); } + @Override public void applyTo(IBaseResource theResouce) { if (theResouce == null) { throw new NullPointerException("theResource can not be null"); @@ -332,12 +332,12 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { return myUnqualifiedVersionId; } + @Override public Long getVersionIdPartAsLong() { if (!hasVersionIdPart()) { return null; - } else { - return Long.parseLong(getVersionIdPart()); } + return Long.parseLong(getVersionIdPart()); } /** @@ -345,6 +345,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { * * @see #getBaseUrl() */ + @Override public boolean hasBaseUrl() { return isNotBlank(myBaseUrl); } @@ -440,7 +441,9 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { /** * Copies the value from the given IdDt to this IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API. + * @deprecated */ + @Deprecated //override deprecated method @Override public void setId(IdDt theId) { setValue(theId.getValue()); @@ -666,15 +669,14 @@ public class IdDt extends UriDt implements IPrimitiveDatatype, IIdType { public static IdDt of(IBaseResource theResouce) { if (theResouce == null) { throw new NullPointerException("theResource can not be null"); + } + IIdType retVal = theResouce.getIdElement(); + if (retVal == null) { + return null; + } else if (retVal instanceof IdDt) { + return (IdDt) retVal; } else { - IIdType retVal = theResouce.getIdElement(); - if (retVal == null) { - return null; - } else if (retVal instanceof IdDt) { - return (IdDt) retVal; - } else { - return new IdDt(retVal.getValue()); - } + return new IdDt(retVal.getValue()); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntrySearchModeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntrySearchModeEnum.java index 735d3306877..ed055699d59 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntrySearchModeEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntrySearchModeEnum.java @@ -90,6 +90,9 @@ public enum BundleEntrySearchModeEnum { * Converts codes to their respective enumerated values */ public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { + + private static final long serialVersionUID = -3836039426814809083L; + @Override public String toCodeString(BundleEntrySearchModeEnum theEnum) { return theEnum.getCode(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryTransactionMethodEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryTransactionMethodEnum.java index 3c6e5fc6122..42fd2b353d8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryTransactionMethodEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryTransactionMethodEnum.java @@ -97,6 +97,9 @@ public enum BundleEntryTransactionMethodEnum { * Converts codes to their respective enumerated values */ public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { + + private static final long serialVersionUID = 7569681479045998433L; + @Override public String toCodeString(BundleEntryTransactionMethodEnum theEnum) { return theEnum.getCode(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleTypeEnum.java index 49ed6266e2a..627109c54f7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleTypeEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleTypeEnum.java @@ -102,6 +102,9 @@ public enum BundleTypeEnum { * Converts codes to their respective enumerated values */ public static final IValueSetEnumBinder VALUESET_BINDER = new IValueSetEnumBinder() { + + private static final long serialVersionUID = -305725916208867517L; + @Override public String toCodeString(BundleTypeEnum theEnum) { return theEnum.getCode(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java index 507a98c6bbb..69a898fe2ba 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java @@ -85,10 +85,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener initialize(theContext); } - String name = null; - if (name == null) { - name = myClassToName.get(theResource.getClass()); - } + String name = myClassToName.get(theResource.getClass()); if (name == null) { name = theContext.getResourceDefinition(theResource).getName().toLowerCase(); } @@ -97,9 +94,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener if (myIgnoreMissingTemplates) { ourLog.debug("No narrative template available for resorce: {}", name); return; - } else { - throw new DataFormatException("No narrative template for class " + theResource.getClass().getCanonicalName()); } + throw new DataFormatException("No narrative template for class " + theResource.getClass().getCanonicalName()); } try { @@ -132,10 +128,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener } theNarrative.setStatusAsString("empty"); return; - } else { + } throw new DataFormatException(e); } - } } protected abstract List getPropertyFile(); @@ -229,6 +224,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener String narrativePropName = name + ".narrative"; String narrativeName = file.getProperty(narrativePropName); if (isBlank(narrativeName)) { + //FIXME resource leak throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName); } @@ -289,6 +285,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener throw new IOException("Can not find '" + cpName + "' on classpath"); } } + //FIXME resource leak return resource; } else if (name.startsWith("file:")) { File file = new File(name.substring("file:".length())); @@ -418,31 +415,30 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener context.setVariable("resource", value); String name = null; - if (value != null) { - Class nextClass = value.getClass(); - do { - name = myClassToName.get(nextClass); - nextClass = nextClass.getSuperclass(); - } while (name == null && nextClass.equals(Object.class) == false); - if (name == null) { - if (value instanceof IBaseResource) { - name = myContext.getResourceDefinition((Class) value).getName(); - } else if (value instanceof IDatatype) { - name = value.getClass().getSimpleName(); - name = name.substring(0, name.length() - 2); - } else if (value instanceof IBaseDatatype) { - name = value.getClass().getSimpleName(); - if (name.endsWith("Type")) { - name = name.substring(0, name.length() - 4); - } - } else { - throw new DataFormatException("Don't know how to determine name for type: " + value.getClass()); - } - name = name.toLowerCase(); - if (!myNameToNarrativeTemplate.containsKey(name)) { - name = null; + Class nextClass = value.getClass(); + do { + name = myClassToName.get(nextClass); + nextClass = nextClass.getSuperclass(); + } while (name == null && nextClass.equals(Object.class) == false); + + if (name == null) { + if (value instanceof IBaseResource) { + name = myContext.getResourceDefinition((Class) value).getName(); + } else if (value instanceof IDatatype) { + name = value.getClass().getSimpleName(); + name = name.substring(0, name.length() - 2); + } else if (value instanceof IBaseDatatype) { + name = value.getClass().getSimpleName(); + if (name.endsWith("Type")) { + name = name.substring(0, name.length() - 4); } + } else { + throw new DataFormatException("Don't know how to determine name for type: " + value.getClass()); + } + name = name.toLowerCase(); + if (!myNameToNarrativeTemplate.containsKey(name)) { + name = null; } } @@ -450,9 +446,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener if (myIgnoreMissingTemplates) { ourLog.debug("No narrative template available for type: {}", value.getClass()); return; - } else { - throw new DataFormatException("No narrative template for class " + value.getClass()); } + throw new DataFormatException("No narrative template for class " + value.getClass()); } String result = myProfileTemplateEngine.process(name, context); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 3c764c9d313..801f061dd68 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -305,24 +305,20 @@ public abstract class BaseParser implements IParser { } } return reference; - } else { - if (!ref.hasResourceType() && !ref.isLocal() && theRef.getResource() != null) { - ref = ref.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName()); - } - if (isNotBlank(myServerBaseUrl) && StringUtils.equals(myServerBaseUrl, ref.getBaseUrl())) { - if (isStripVersionsFromReferences(theCompositeChildElement)) { - return ref.toUnqualifiedVersionless().getValue(); - } else { - return ref.toUnqualified().getValue(); - } - } else { - if (isStripVersionsFromReferences(theCompositeChildElement)) { - return ref.toVersionless().getValue(); - } else { - return ref.getValue(); - } - } } + if (!ref.hasResourceType() && !ref.isLocal() && theRef.getResource() != null) { + ref = ref.withResourceType(myContext.getResourceDefinition(theRef.getResource()).getName()); + } + if (isNotBlank(myServerBaseUrl) && StringUtils.equals(myServerBaseUrl, ref.getBaseUrl())) { + if (isStripVersionsFromReferences(theCompositeChildElement)) { + return ref.toUnqualifiedVersionless().getValue(); + } + return ref.toUnqualified().getValue(); + } + if (isStripVersionsFromReferences(theCompositeChildElement)) { + return ref.toVersionless().getValue(); + } + return ref.getValue(); } private boolean isStripVersionsFromReferences(CompositeChildElement theCompositeChildElement) { @@ -1112,9 +1108,8 @@ public abstract class BaseParser implements IParser { } if (theElements.contains(thePathBuilder.toString())) { return true; - } else { - return false; } + return false; } else if (myParent != null) { boolean parentCheck; if (theCheckingForWhitelist) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 782907c977d..792bb597db6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -25,37 +25,74 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; -import java.io.PushbackReader; import java.io.Reader; import java.io.Writer; import java.math.BigDecimal; -import java.util.*; -import java.util.Map.Entry; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.text.WordUtils; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBinary; +import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; +import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; +import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.INarrative; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeChildContainedResources; +import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; +import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; +import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.BaseBundle; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleEntry; +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IPrimitiveDatatype; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.api.Tag; +import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt; -import ca.uhn.fhir.model.primitive.*; +import ca.uhn.fhir.model.primitive.DecimalDt; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.model.primitive.InstantDt; +import ca.uhn.fhir.model.primitive.IntegerDt; +import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.parser.json.GsonStructure; import ca.uhn.fhir.parser.json.JsonLikeArray; import ca.uhn.fhir.parser.json.JsonLikeObject; import ca.uhn.fhir.parser.json.JsonLikeStructure; import ca.uhn.fhir.parser.json.JsonLikeValue; -import ca.uhn.fhir.parser.json.JsonLikeWriter; import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType; import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; +import ca.uhn.fhir.parser.json.JsonLikeWriter; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.util.ElementUtil; @@ -110,9 +147,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } theListToAddTo.get(valueIdx).addAll(theCommentsToAdd); return true; - } else { - return false; } + return false; } private boolean addToHeldExtensions(int valueIdx, List> ext, ArrayList> list, boolean theIsModifier, CompositeChildElement theChildElem) { @@ -128,9 +164,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { list.get(valueIdx).add(new HeldExtension(next, theIsModifier, theChildElem)); } return true; - } else { - return false; } + return false; } private void addToHeldIds(int theValueIdx, ArrayList theListToAddTo, String theId) { @@ -143,7 +178,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } } - private void assertObjectOfType(JsonLikeValue theResourceTypeObj, Object theValueType, String thePosition) { +// private void assertObjectOfType(JsonLikeValue theResourceTypeObj, Object theValueType, String thePosition) { // if (theResourceTypeObj == null) { // throw new DataFormatException("Invalid JSON content detected, missing required element: '" + thePosition + "'"); // } @@ -151,7 +186,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { // if (theResourceTypeObj.getValueType() != theValueType) { // throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType); // } - } +// } private void beginArray(JsonLikeWriter theEventWriter, String arrayName) throws IOException { theEventWriter.beginArray(arrayName); @@ -281,6 +316,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { writeTagWithTextNode(theEventWriter, "deleted", nextEntry.getDeletedAt()); } writeTagWithTextNode(theEventWriter, "title", nextEntry.getTitle()); + //TODO: Use of a deprecated method should be resolved. writeTagWithTextNode(theEventWriter, "id", nextEntry.getId()); linkStarted = false; @@ -291,6 +327,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { theEventWriter.endArray(); } + //TODO: Use of a deprecated method should be resolved. writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated()); writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished()); @@ -575,7 +612,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } if (narr != null && narr.isEmpty()) { gen.generateNarrative(myContext, theResource, narr); - if (narr != null && !narr.isEmpty()) { + if (!narr.isEmpty()) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; String childName = nextChild.getChildNameByDatatype(child.getDatatype()); BaseRuntimeElementDefinition type = child.getChildByName(childName); @@ -980,7 +1017,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) { for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { if (nextValue != null) { - if (nextValue == null || nextValue.isEmpty()) { + if (nextValue.isEmpty()) { continue; } extensions.add(new HeldExtension(nextDef, nextValue, theChildElem)); @@ -990,7 +1027,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) { for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { if (nextValue != null) { - if (nextValue == null || nextValue.isEmpty()) { + if (nextValue.isEmpty()) { continue; } modifierExtensions.add(new HeldExtension(nextDef, nextValue, theChildElem)); @@ -1106,7 +1143,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } JsonLikeValue alternateVal = theAlternateVal; - if (alternateVal != null && alternateVal.isObject() == false) { + if (alternateVal.isObject() == false) { getErrorHandler().incorrectJsonType(null, theAlternateName, ValueType.OBJECT, null, alternateVal.getJsonType(), null); return; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 9750f68f511..02a26311d39 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -142,6 +142,7 @@ class ParserState { if (entry.getLinkSelf() != null && entry.getLinkSelf().isEmpty() == false) { id = new IdDt(entry.getLinkSelf().getValue()); } else { + //TODO: Use of a deprecated method should be resolved. id = entry.getId(); } @@ -159,6 +160,7 @@ class ParserState { if (resource != null) { resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt()); + //TODO: Use of a deprecated method should be resolved. resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id); } } @@ -364,6 +366,7 @@ class ParserState { @Override public void attributeValue(String theName, String theValue) throws DataFormatException { if ("ref".equals(theName)) { + //TODO: Use of a deprecated method should be resolved. getEntry().setId(new IdDt(theValue)); } else if ("when".equals(theName)) { getEntry().setDeleted(new InstantDt(theValue)); @@ -451,10 +454,12 @@ class ParserState { if ("title".equals(theLocalPart)) { push(new AtomPrimitiveState(myEntry.getTitle())); } else if ("id".equals(theLocalPart)) { + //TODO: Use of a deprecated method should be resolved. push(new AtomPrimitiveState(myEntry.getId())); } else if ("link".equals(theLocalPart)) { push(new AtomLinkState(myEntry)); } else if ("updated".equals(theLocalPart)) { + //TODO: Use of a deprecated method should be resolved. push(new AtomPrimitiveState(myEntry.getUpdated())); } else if ("published".equals(theLocalPart)) { push(new AtomPrimitiveState(myEntry.getPublished())); @@ -771,7 +776,7 @@ class ParserState { myErrorHandler.unknownAttribute(null, theName); } - public boolean elementIsRepeating(@SuppressWarnings("unused") String theChildName) { + public boolean elementIsRepeating(String theChildName) { return false; } @@ -790,7 +795,6 @@ class ParserState { /** * Default implementation just handles undeclared extensions */ - @SuppressWarnings("unused") public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) { if (myPreResourceState != null && getCurrentElement() instanceof ISupportsUndeclaredExtensions) { ExtensionDt newExtension = new ExtensionDt(theIsModifier); @@ -1031,6 +1035,7 @@ class ParserState { @Override public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException { if ("base".equals(theLocalPart)) { + //TODO: Use of a deprecated method should be resolved. push(new PrimitiveState(getPreResourceState(), myEntry.getLinkBase())); } else if ("request".equals(theLocalPart)) { push(new BundleEntryTransactionState(myEntry)); @@ -1302,6 +1307,7 @@ class ParserState { } String bundleBaseUrl = myInstance.getLinkBase().getValue(); + //TODO: Use of a deprecated method should be resolved. String entryBaseUrl = nextEntry.getLinkBase().getValue(); String version = ResourceMetadataKeyEnum.VERSION.get(nextResource); String resourceName = myContext.getResourceDefinition(nextResource).getName(); @@ -2030,9 +2036,8 @@ class ParserState { private PreResourceState getRootPreResourceState() { if (getPreResourceState() != null) { return getPreResourceState(); - } else { - return this; } + return this; } @Override @@ -2590,8 +2595,7 @@ class ParserState { public void attributeValue(String theName, String theValue) throws DataFormatException { if (myJsonMode) { myDt.setValueAsString(theValue); - return; - } else { + } else { // IGNORE - don't handle this as an error, we process these as XML events } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 19f402c0125..516e53cb6cb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -45,17 +45,14 @@ import javax.xml.stream.events.Namespace; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; -import ca.uhn.fhir.model.api.BaseBundle; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseElement; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; -import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseXhtml; import org.hl7.fhir.instance.model.api.IDomainResource; @@ -65,17 +62,16 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition; -import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeChildContainedResources; -import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildExtension; import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.BaseBundle; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; @@ -101,7 +97,7 @@ import ca.uhn.fhir.util.XmlUtil; * This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use * {@link FhirContext#newXmlParser()} to get an instance. */ -public class XmlParser extends BaseParser implements IParser { +public class XmlParser extends BaseParser /*implements IParser */{ static final String ATOM_NS = "http://www.w3.org/2005/Atom"; static final String FHIR_NS = "http://hl7.org/fhir"; @@ -149,10 +145,9 @@ public class XmlParser extends BaseParser implements IParser { if (myPrettyPrint) { PrettyPrintWriterWrapper retVal = new PrettyPrintWriterWrapper(eventWriter); return retVal; - } else { - NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter); - return retVal; } + NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter); + return retVal; } @Override @@ -330,6 +325,7 @@ public class XmlParser extends BaseParser implements IParser { eventWriter.writeNamespace("at", TOMBSTONES_NS); if (nextEntry.getDeletedResourceId().isEmpty()) { + //TODO: Use of a deprecated method should be resolved. writeOptionalAttribute(eventWriter, "ref", nextEntry.getId().getValueAsString()); } else { writeOptionalAttribute(eventWriter, "ref", nextEntry.getDeletedResourceId().getValueAsString()); @@ -361,12 +357,15 @@ public class XmlParser extends BaseParser implements IParser { writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle()); if (!deleted) { + //TODO: Use of a deprecated method should be resolved. if (nextEntry.getId().isEmpty() == false) { + //TODO: Use of a deprecated method should be resolved. writeTagWithTextNode(eventWriter, "id", nextEntry.getId()); } else { writeTagWithTextNode(eventWriter, "id", nextEntry.getResource().getId()); } } + //TODO: Use of a deprecated method should be resolved. writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated()); writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished()); @@ -420,6 +419,7 @@ public class XmlParser extends BaseParser implements IParser { IdDt bundleId = theBundle.getId(); if (bundleId != null && isNotBlank(bundleId.getVersionIdPart()) || (updated != null && !updated.isEmpty())) { theEventWriter.writeStartElement("meta"); + //FIXME potential null acces bundleId may be null at this time due to the OR clause writeOptionalTagWithValue(theEventWriter, "versionId", bundleId.getVersionIdPart()); if (updated != null) { writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); @@ -476,8 +476,11 @@ public class XmlParser extends BaseParser implements IParser { if (deleted) { theEventWriter.writeStartElement("deleted"); + //TODO: Use of a deprecated method should be resolved. writeOptionalTagWithValue(theEventWriter, "type", nextEntry.getId().getResourceType()); + //TODO: Use of a deprecated method should be resolved. writeOptionalTagWithValue(theEventWriter, "id", nextEntry.getId().getIdPart()); + //TODO: Use of a deprecated method should be resolved. writeOptionalTagWithValue(theEventWriter, "versionId", nextEntry.getId().getVersionIdPart()); writeOptionalTagWithValue(theEventWriter, "instant", nextEntry.getDeletedAt().getValueAsString()); theEventWriter.writeEndElement(); @@ -504,18 +507,16 @@ public class XmlParser extends BaseParser implements IParser { switch (childDef.getChildType()) { case ID_DATATYPE: { - IIdType value = (IIdType) theElement; + IIdType value = IIdType.class.cast(theElement); String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue(); - if (value != null) { - theEventWriter.writeStartElement(childName); - theEventWriter.writeAttribute("value", encodedValue); - encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); - theEventWriter.writeEndElement(); - } + theEventWriter.writeStartElement(childName); + theEventWriter.writeAttribute("value", encodedValue); + encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); + theEventWriter.writeEndElement(); break; } case PRIMITIVE_DATATYPE: { - IPrimitiveType pd = (IPrimitiveType) theElement; + IPrimitiveType pd = IPrimitiveType.class.cast(theElement); String value = pd.getValueAsString(); if (value != null || super.hasExtensions(pd)) { theEventWriter.writeStartElement(childName); @@ -568,23 +569,21 @@ public class XmlParser extends BaseParser implements IParser { break; } case PRIMITIVE_XHTML: { - XhtmlDt dt = (XhtmlDt) theElement; + XhtmlDt dt = XhtmlDt.class.cast(theElement); if (dt.hasContent()) { encodeXhtml(dt, theEventWriter); } break; } case PRIMITIVE_XHTML_HL7ORG: { - IBaseXhtml dt = (IBaseXhtml) theElement; - if (dt.isEmpty()) { - break; - } else { + IBaseXhtml dt = IBaseXhtml.class.cast(theElement); + if (!dt.isEmpty()) { // TODO: this is probably not as efficient as it could be XhtmlDt hdt = new XhtmlDt(); hdt.setValueAsString(dt.getValueAsString()); encodeXhtml(hdt, theEventWriter); - break; } + break; } case EXTENSION_DECLARED: case UNDECL_EXT: { @@ -621,6 +620,7 @@ public class XmlParser extends BaseParser implements IParser { } else { narr = null; } + //FIXME potential null access on narr see line 623 if (gen != null && narr.isEmpty()) { gen.generateNarrative(myContext, theResource, narr); } @@ -914,9 +914,8 @@ public class XmlParser extends BaseParser implements IParser { childDef = myContext.getElementDefinition(value.getClass()); if (childDef == null) { throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName()); - } else { - childName = RuntimeChildUndeclaredExtensionDefinition.createExtensionChildName(childDef); - } + } + childName = RuntimeChildUndeclaredExtensionDefinition.createExtensionChildName(childDef); } else { childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); if (childDef == null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Delete.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Delete.java index 58eb2fe01b9..59caaab86ab 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Delete.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Delete.java @@ -27,8 +27,6 @@ import java.lang.annotation.Target; import org.hl7.fhir.instance.model.api.IBaseResource; -import ca.uhn.fhir.model.api.IResource; - /** * RESTful method annotation to be used for the FHIR * delete method. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index 0cb93da1a37..9def56fb5cd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -323,6 +323,7 @@ public abstract class BaseClient implements IRestfulClient { } } catch (DataFormatException e) { + //FIXME potential null access on httpResquest String msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString()); throw new FhirClientConnectionException(msg, e); } catch (IllegalStateException e) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 511ff147aad..67251bdbd0a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -87,6 +87,7 @@ public class GenericClient extends BaseClient implements IGenericClient { myContext = theContext; } + @Deprecated //override deprecated method @Override public IBaseConformance conformance() { if (myContext.getVersion().getVersion().isRi()) { @@ -111,6 +112,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return new CreateInternal(); } + @Deprecated //overide deprecated method @Override public MethodOutcome create(IBaseResource theResource) { BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext); @@ -133,6 +135,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return new DeleteInternal(); } + @Deprecated //override deprecated method @Override public MethodOutcome delete(final Class theType, IdDt theId) { HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), theId.withResourceType(toResourceName(theType))); @@ -146,6 +149,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return resp; } + @Deprecated //override deprecated method @Override public MethodOutcome delete(Class theType, String theId) { return delete(theType, new IdDt(theId)); @@ -186,12 +190,11 @@ public class GenericClient extends BaseClient implements IGenericClient { if (theNotModifiedHandler == null) { return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } else { - try { - return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } catch (NotModifiedException e) { - return theNotModifiedHandler.call(); - } + } + try { + return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); + } catch (NotModifiedException e) { + return theNotModifiedHandler.call(); } } @@ -245,6 +248,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return new HistoryInternal(); } + @Deprecated //override deprecated method @Override public Bundle history(final Class theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) { String resourceName = theType != null ? toResourceName(theType) : null; @@ -260,6 +264,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } + @Deprecated //override deprecated method @Override public Bundle history(Class theType, String theId, DateTimeDt theSince, Integer theLimit) { return history(theType, new IdDt(theId), theSince, theLimit); @@ -409,6 +414,7 @@ public class GenericClient extends BaseClient implements IGenericClient { myLastRequest = theLastRequest; } + @Deprecated //override deprecated method @Override public void setLogRequestAndResponse(boolean theLogRequestAndResponse) { myLogRequestAndResponse = theLogRequestAndResponse; @@ -423,6 +429,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return new TransactionInternal(); } + @Deprecated //override deprecated method @Override public List transaction(List theResources) { BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext); @@ -569,6 +576,7 @@ public class GenericClient extends BaseClient implements IGenericClient { protected SummaryEnum mySummaryMode; + @Deprecated //override deprecated method @SuppressWarnings("unchecked") @Override public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { @@ -612,9 +620,8 @@ public class GenericClient extends BaseClient implements IGenericClient { public List> getPreferResponseTypes(Class theDefault) { if (myPreferResponseTypes != null) { return myPreferResponseTypes; - } else { - return toTypeList(theDefault); } + return toTypeList(theDefault); } protected HashSet getSubsetElements() { @@ -1207,6 +1214,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return nextOrPrevious(PREVIOUS, theBundle); } + @Deprecated //override deprecated method @Override public IGetPageTyped url(String thePageUrl) { return new GetPageInternal(thePageUrl); @@ -1446,29 +1454,27 @@ public class GenericClient extends BaseClient implements IGenericClient { handler = new ResourceResponseHandler(myReturnResourceType); Object retVal = invoke(null, handler, invocation); return retVal; - } else { - ResourceResponseHandler handler; - handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - - Object retVal = invoke(null, handler, invocation); - if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { - return retVal; - } else { - RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); - IBaseResource parameters = def.newInstance(); - - BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - IBase parameter = paramChildElem.newInstance(); - paramChild.getMutator().addValue(parameters, parameter); - - BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); - resourceElem.getMutator().addValue(parameter, (IBase) retVal); - - return parameters; - } } + ResourceResponseHandler handler; + handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); + + Object retVal = invoke(null, handler, invocation); + if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { + return retVal; + } + RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); + IBaseResource parameters = def.newInstance(); + + BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); + IBase parameter = paramChildElem.newInstance(); + paramChild.getMutator().addValue(parameters, parameter); + + BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); + resourceElem.getMutator().addValue(parameter, (IBase) retVal); + + return parameters; } @Override @@ -1591,10 +1597,10 @@ public class GenericClient extends BaseClient implements IGenericClient { private final class OutcomeResponseHandler implements IClientResponseHandler { private PreferReturnEnum myPrefer; - private final String myResourceName; +// private final String myResourceName; private OutcomeResponseHandler(String theResourceName) { - myResourceName = theResourceName; +// myResourceName = theResourceName; } private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) { @@ -1634,9 +1640,8 @@ public class GenericClient extends BaseClient implements IGenericClient { public Object execute() {// AAA if (myId.hasVersionIdPart()) { return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); - } else { - return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); } + return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); } @Override @@ -1774,9 +1779,8 @@ public class GenericClient extends BaseClient implements IGenericClient { IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); bundleFactory.initializeWithBundleResource(response); return bundleFactory.toListOfResources(); - } else { - return new ArrayList(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); } + return new ArrayList(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); } } @@ -1850,6 +1854,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return this; } + @Deprecated //override deprecated method @Override public IBase execute() { @@ -1906,8 +1911,11 @@ public class GenericClient extends BaseClient implements IGenericClient { if (rootSs == null) { rootSs = nextSortSpec; } else { + //FIXME lastSs is null never set + //TODO unused assignment lastSs.setChain(nextSortSpec); } + //TODO unused assignment lastSs = nextSortSpec; } if (rootSs != null) { @@ -1983,6 +1991,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return this; } + @Deprecated //override deprecated method @Override public IQuery limitTo(int theLimitTo) { return count(theLimitTo); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index ba3f19c0e64..00f023e3502 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -318,12 +318,13 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { String serverFhirVersionString = null; Object value = t.getSingleValueOrNull(conformance, "fhirVersion"); if (value instanceof IPrimitiveType) { - serverFhirVersionString = ((IPrimitiveType) value).getValueAsString(); + serverFhirVersionString = IPrimitiveType.class.cast(value).getValueAsString(); } FhirVersionEnum serverFhirVersionEnum = null; if (StringUtils.isBlank(serverFhirVersionString)) { // we'll be lenient and accept this } else { + //FIXME null access on serverFhirVersionString if (serverFhirVersionString.startsWith("0.80") || serverFhirVersionString.startsWith("0.0.8")) { serverFhirVersionEnum = FhirVersionEnum.DSTU1; } else if (serverFhirVersionString.startsWith("0.4")) { @@ -347,11 +348,13 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { } + @Deprecated //override deprecated method @Override public ServerValidationModeEnum getServerValidationModeEnum() { return getServerValidationMode(); } + @Deprecated //override deprecated method @Override public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) { setServerValidationMode(theServerValidationMode); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java index 7fb1c8ed98f..be1af48e8b0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java @@ -30,7 +30,13 @@ import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -47,7 +53,6 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.CoverageIgnore; -import ca.uhn.fhir.util.VersionUtil; /** * A Http Client based on Apache. This is an adapter around the class diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java index 0859096690a..e0023673d2a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpRequest.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.client.apache; */ import java.io.IOException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -92,7 +93,8 @@ public class ApacheHttpRequest implements IHttpRequest { if (myRequest instanceof HttpEntityEnclosingRequest) { HttpEntity entity = ((HttpEntityEnclosingRequest) myRequest).getEntity(); if (entity.isRepeatable()) { - return IOUtils.toString(entity.getContent()); + //TODO verify the charset + return IOUtils.toString(entity.getContent(), Charset.defaultCharset()); } } return null; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java index 939afeeaccf..9dc17f33068 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java @@ -61,6 +61,7 @@ public class ApacheHttpResponse implements IHttpResponse { this.myResponse = theResponse; } + @Deprecated //override deprecated method @Override public void bufferEntitity() throws IOException { bufferEntity(); @@ -77,6 +78,7 @@ public class ApacheHttpResponse implements IHttpResponse { try { this.myEntityBytes = IOUtils.toByteArray(respEntity); } catch (IllegalStateException e) { + //FIXME resouce leak throw new InternalErrorException(e); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java index b1819788b78..20337994173 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java @@ -73,12 +73,14 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { public synchronized HttpClient getNativeHttpClient() { if (myHttpClient == null) { + //FIXME potential resoource leak PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); connectionManager.setMaxTotal(getPoolMaxTotal()); connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute()); // @formatter:off + //TODO: Use of a deprecated method should be resolved. RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(getSocketTimeout()) .setConnectTimeout(getConnectTimeout()).setConnectionRequestTimeout(getConnectionRequestTimeout()) .setStaleConnectionCheckEnabled(true).setProxy(myProxy).build(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java index e844d96cb13..be47354efd3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java @@ -120,6 +120,7 @@ public class LoggingInterceptor implements IClientInterceptor { } if (myLogResponseBody) { + //TODO: Use of a deprecated method should be resolved. theResponse.bufferEntitity(); InputStream respEntity = null; try { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java index 53e08a67891..156cdee4672 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/DateClientParam.java @@ -27,7 +27,6 @@ import java.util.Date; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; -import ca.uhn.fhir.rest.gclient.DateClientParam.IDateSpecifier; import ca.uhn.fhir.rest.param.ParamPrefixEnum; /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringCriterion.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringCriterion.java index 1085092bd8c..d22a2b29a0b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringCriterion.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringCriterion.java @@ -3,7 +3,6 @@ package ca.uhn.fhir.rest.gclient; import static org.apache.commons.lang3.StringUtils.isBlank; import java.util.Collection; -import java.util.List; import org.apache.commons.lang3.StringUtils; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java index c56ecce41c8..b220d8ea997 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java @@ -21,7 +21,6 @@ package ca.uhn.fhir.rest.method; */ import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.util.List; import ca.uhn.fhir.context.ConfigurationException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java index b0e80a30c94..659866775e5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java @@ -200,12 +200,11 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca if (myParams != null) { return httpClient.createParamRequest(getContext(), myParams, encoding); - } else { - encoding = ObjectUtils.defaultIfNull(encoding, EncodingEnum.XML); - String contents = encodeContents(thePrettyPrint, encoding); - String contentType = getContentType(encoding); - return httpClient.createByteRequest(getContext(), contents, contentType, encoding); } + encoding = ObjectUtils.defaultIfNull(encoding, EncodingEnum.XML); + String contents = encodeContents(thePrettyPrint, encoding); + String contentType = getContentType(encoding); + return httpClient.createByteRequest(getContext(), contents, contentType, encoding); } private String getContentType(EncodingEnum encoding) { @@ -253,10 +252,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca Bundle bundle = bundleFactory.getDstu1Bundle(); if (bundle != null) { return parser.encodeBundleToString(bundle); - } else { - IBaseResource bundleRes = bundleFactory.getResourceBundle(); - return parser.encodeResourceToString(bundleRes); } + IBaseResource bundleRes = bundleFactory.getResourceBundle(); + return parser.encodeResourceToString(bundleRes); } else if (myContents != null) { return myContents; } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index 12111548400..04983df24c7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -259,9 +259,8 @@ public abstract class BaseMethodBinding implements IClientResponseHandler } catch (InvocationTargetException e) { if (e.getCause() instanceof BaseServerResponseException) { throw (BaseServerResponseException) e.getCause(); - } else { - throw new InternalErrorException("Failed to call access method", e); } + throw new InternalErrorException("Failed to call access method", e); } catch (Exception e) { throw new InternalErrorException("Failed to call access method", e); } @@ -455,6 +454,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler if (returnTypeFromRp != null) { if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) { if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) { + //FIXME potential null access on retunrTypeFromMethod throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract"); } @@ -492,9 +492,8 @@ public abstract class BaseMethodBinding implements IClientResponseHandler if (search.dynamic()) { IDynamicSearchResourceProvider provider = (IDynamicSearchResourceProvider) theProvider; return new DynamicSearchMethodBinding(returnType, theMethod, theContext, provider); - } else { - return new SearchMethodBinding(returnType, theMethod, theContext, theProvider); } + return new SearchMethodBinding(returnType, theMethod, theContext, theProvider); } else if (conformance != null) { return new ConformanceMethodBinding(theMethod, theContext, theProvider); } else if (create != null) { @@ -510,9 +509,8 @@ public abstract class BaseMethodBinding implements IClientResponseHandler } else if (validate != null) { if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { return new ValidateMethodBindingDstu1(theMethod, theContext, theProvider); - } else { - return new ValidateMethodBindingDstu2Plus(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate); } + return new ValidateMethodBindingDstu2Plus(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate); } else if (getTags != null) { return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags); } else if (addTags != null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index e007da44676..a63ec352a63 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -101,16 +101,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding list = dstu1bundle.toListOfResources(); if (list.size() == 0) { return null; @@ -272,25 +273,24 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null, theRequest.isRespondGzip(), isAddContentLocationHeader()); - } else { - // Is this request coming from a browser - String uaHeader = theRequest.getHeader("user-agent"); - boolean requestIsBrowser = false; - if (uaHeader != null && uaHeader.contains("Mozilla")) { - requestIsBrowser = true; - } - - for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) { - IServerInterceptor next = theServer.getInterceptors().get(i); - boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle()); - if (!continueProcessing) { - ourLog.debug("Interceptor {} returned false, not continuing processing"); - return null; - } - } - - return theRequest.getResponse().streamResponseAsBundle(responseObject.getDstu1Bundle(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser); + } + // Is this request coming from a browser + String uaHeader = theRequest.getHeader("user-agent"); + boolean requestIsBrowser = false; + if (uaHeader != null && uaHeader.contains("Mozilla")) { + requestIsBrowser = true; } + + for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) { + IServerInterceptor next = theServer.getInterceptors().get(i); + boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle()); + if (!continueProcessing) { + ourLog.debug("Interceptor {} returned false, not continuing processing"); + return null; + } + } + + return theRequest.getResponse().streamResponseAsBundle(responseObject.getDstu1Bundle(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser); } public ResourceOrDstu1Bundle doInvokeServer(IRestfulServer theServer, RequestDetails theRequest) { @@ -363,8 +363,6 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi bundleFactory.addRootPropertiesToBundle(null, theRequest.getFhirServerBase(), linkSelf, count, getResponseBundleType(), lastUpdated); responseObject = new ResourceOrDstu1Bundle(resource); - break; - } else { Set includes = getRequestIncludesFromParams(params); @@ -394,9 +392,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi IBaseResource resBundle = bundleFactory.getResourceBundle(); responseObject = new ResourceOrDstu1Bundle(resBundle); } - - break; } + break; } case RESOURCE: { IBundleProvider result = (IBundleProvider) resultObj; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java index 91f1940505a..a989ff419b0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConditionalParamBinder.java @@ -1,32 +1,10 @@ package ca.uhn.fhir.rest.method; -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2017 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -import static org.apache.commons.lang3.StringUtils.isBlank; - import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -34,7 +12,6 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConformanceMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConformanceMethodBinding.java index 48169362690..709fef4e3c9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConformanceMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ConformanceMethodBinding.java @@ -93,9 +93,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding if ("metadata".equals(theRequest.getOperation())) { if (theRequest.getRequestType() == RequestTypeEnum.GET) { return true; - } else { - throw new MethodNotAllowedException("/metadata request must use HTTP GET", RequestTypeEnum.GET); } + throw new MethodNotAllowedException("/metadata request must use HTTP GET", RequestTypeEnum.GET); } return false; 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 3aa901a4798..1a01636467b 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 @@ -86,7 +86,7 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi } @Override - public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { + public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { if (myIdParamIndex != null) { theMethodParams[myIdParamIndex] = theRequest.getId(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java index 9e0c61c737b..44483bc79a9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ElementsParameter.java @@ -113,9 +113,8 @@ public class ElementsParameter implements IParameter { retVal.add("meta"); return retVal; - } else { - return null; } + return null; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java index 8a6600e8b62..92d9d94dfdf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java @@ -1,27 +1,5 @@ package ca.uhn.fhir.rest.method; -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2017 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import static org.apache.commons.lang3.StringUtils.defaultString; - import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.util.ReflectionUtil; 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 026b0babe55..9bea10da060 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 @@ -156,12 +156,9 @@ public class GetTagsMethodBinding extends BaseMethodBinding { IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode, null); TagList retVal = parser.parseTagList(theResponseReader); return retVal; - } else { - throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader); } - + throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader); } - @Override public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException { Object[] params = createParametersForServerRequest(theRequest); 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 decb2c1a88d..77fd2f2ec57 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 @@ -171,7 +171,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding { return new IBundleProvider() { @Override - public InstantDt getPublished() { + public IPrimitiveType getPublished() { return resources.getPublished(); } @@ -184,6 +184,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding { throw new InternalErrorException("Server provided resource at index " + index + " with no ID set (using IResource#setId(IdDt))"); } if (isBlank(nextResource.getIdElement().getVersionIdPart()) && nextResource instanceof IResource) { + //TODO: Use of a deprecated method should be resolved. IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get((IResource) nextResource); if (versionId == null || versionId.isEmpty()) { throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#setId(IdDt))"); @@ -201,7 +202,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding { @Override public Integer preferredPageSize() { - return null; + return resources.preferredPageSize(); + } + + @Override + public String getUuid() { + return resources.getUuid(); } }; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java index 90e86b934f2..64123323625 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IncludeParameter.java @@ -149,12 +149,11 @@ class IncludeParameter extends BaseQueryParameter { if (myInstantiableCollectionType == null) { if (mySpecType == String.class) { return value; - } else { - return new Include(value, recurse); } - } else { - retValCollection.add(new Include(value, recurse)); + return new Include(value, recurse); } + //FIXME null access + retValCollection.add(new Include(value, recurse)); } return retValCollection; 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 0a1ec69dbfd..5ee93f41ebe 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 @@ -482,9 +482,8 @@ public class MethodUtil { OperationParameter.throwInvalidMode(theObject.toString()); } return retVal; - } else { - return null; } + return null; } @Override @@ -671,6 +670,7 @@ public class MethodUtil { break; } + //FIXME null access return binder.parse(theContext, theUnqualifiedParamName, theParameters); } 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 df89dcdb5bd..b4024c77d03 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 @@ -281,10 +281,9 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { if (!myIdempotent) { String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); throw new MethodNotAllowedException(message, RequestTypeEnum.POST); - } else { - String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name()); - throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST); } + String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name()); + throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST); } if (myIdParamIndex != null) { @@ -341,34 +340,33 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { if (!theUseHttpGet) { return new HttpPostClientInvocation(theContext, theInput, b.toString()); - } else { - FhirTerser t = theContext.newTerser(); - List parameters = t.getValues(theInput, "Parameters.parameter"); - - Map> params = new LinkedHashMap>(); - for (Object nextParameter : parameters) { - IPrimitiveType nextNameDt = (IPrimitiveType) t.getSingleValueOrNull((IBase) nextParameter, "name"); - if (nextNameDt == null || nextNameDt.isEmpty()) { - ourLog.warn("Ignoring input parameter with no value in Parameters.parameter.name in operation client invocation"); - continue; - } - String nextName = nextNameDt.getValueAsString(); - if (!params.containsKey(nextName)) { - params.put(nextName, new ArrayList()); - } - - IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]"); - if (value == null) { - continue; - } - if (!(value instanceof IPrimitiveType)) { - throw new IllegalArgumentException("Can not invoke operation as HTTP GET when it has parameters with a composite (non priitive) datatype as the value. Found value: " + value.getClass().getName()); - } - IPrimitiveType primitive = (IPrimitiveType) value; - params.get(nextName).add(primitive.getValueAsString()); - } - return new HttpGetClientInvocation(theContext, params, b.toString()); } + FhirTerser t = theContext.newTerser(); + List parameters = t.getValues(theInput, "Parameters.parameter"); + + Map> params = new LinkedHashMap>(); + for (Object nextParameter : parameters) { + IPrimitiveType nextNameDt = (IPrimitiveType) t.getSingleValueOrNull((IBase) nextParameter, "name"); + if (nextNameDt == null || nextNameDt.isEmpty()) { + ourLog.warn("Ignoring input parameter with no value in Parameters.parameter.name in operation client invocation"); + continue; + } + String nextName = nextNameDt.getValueAsString(); + if (!params.containsKey(nextName)) { + params.put(nextName, new ArrayList()); + } + + IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]"); + if (value == null) { + continue; + } + if (!(value instanceof IPrimitiveType)) { + throw new IllegalArgumentException("Can not invoke operation as HTTP GET when it has parameters with a composite (non priitive) datatype as the value. Found value: " + value.getClass().getName()); + } + IPrimitiveType primitive = (IPrimitiveType) value; + params.get(nextName).add(primitive.getValueAsString()); + } + return new HttpGetClientInvocation(theContext, params, b.toString()); } public static class ReturnType { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java index d179fb45f13..a5d2162de53 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java @@ -136,9 +136,8 @@ public class OperationParameter implements IParameter { public String getSearchParamType() { if (mySearchParameterBinding != null) { return mySearchParameterBinding.getParamType().getCode(); - } else { - return null; } + return null; } @SuppressWarnings("unchecked") @@ -326,7 +325,7 @@ public class OperationParameter implements IParameter { } else { for (String nextValue : paramValues) { FhirContext ctx = theRequest.getServer().getFhirContext(); - RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition((Class) myParameterType); + RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) ctx.getElementDefinition(myParameterType.asSubclass(IBase.class)); IPrimitiveType instance = def.newInstance(); instance.setValueAsString(nextValue); matchingParamValues.add(instance); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PageMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PageMethodBinding.java index 40e15970d25..c5b449e4d34 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PageMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PageMethodBinding.java @@ -81,9 +81,8 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding { IBase bundle = handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]); if (bundle instanceof Bundle) { return new ResourceOrDstu1Bundle((Bundle) bundle); - } else { - return new ResourceOrDstu1Bundle((IBaseResource) bundle); } + return new ResourceOrDstu1Bundle((IBaseResource) bundle); } private IBase handlePagingRequest(IRestfulServer theServer, RequestDetails theRequest, String thePagingAction) { @@ -144,9 +143,8 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding { Bundle bundle = bundleFactory.getDstu1Bundle(); if (bundle != null) { return bundle; - } else { - return bundleFactory.getResourceBundle(); } + return bundleFactory.getResourceBundle(); // if (bundle != null) { // for (int i = getInterceptors().size() - 1; i >= 0; i--) { // IServerInterceptor next = getInterceptors().get(i); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ParseAction.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ParseAction.java index 36236eceab1..c820a12f8e8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ParseAction.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ParseAction.java @@ -23,8 +23,6 @@ package ca.uhn.fhir.rest.method; import java.io.IOException; import java.io.Writer; -import org.hl7.fhir.instance.model.api.IBaseResource; - import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.parser.IParser; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PatchTypeParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PatchTypeParameter.java index 6aac25b7fc6..89782c42a29 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PatchTypeParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/PatchTypeParameter.java @@ -34,7 +34,6 @@ import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; class PatchTypeParameter implements IParameter { @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QualifiedParamList.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QualifiedParamList.java index 0cea1a13b55..61ec7d7c43e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QualifiedParamList.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QualifiedParamList.java @@ -120,9 +120,8 @@ public class QualifiedParamList extends ArrayList { char nextChar = theString.charAt(i); if (nextChar != '\\') { break; - } else { - retVal++; } + retVal++; } return retVal; } 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 d88f573ea70..abe7fd75105 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 @@ -99,9 +99,8 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) { if (mySupportsVersion && theRequestDetails.getId().hasVersionIdPart()) { return RestOperationTypeEnum.VREAD; - } else { - return RestOperationTypeEnum.READ; } + return RestOperationTypeEnum.READ; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestOperationCallbackParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestOperationCallbackParameter.java index 047123dc366..1e9ba5e8f7c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestOperationCallbackParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestOperationCallbackParameter.java @@ -30,11 +30,10 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.CoverageIgnore; class RequestOperationCallbackParameter implements IParameter { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class); +// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class); @CoverageIgnore @Override 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 3ca67891b3f..97f2d571313 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 @@ -225,9 +225,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { if (!myQueryName.equals(queryName)) { ourLog.trace("Query name does not match {}", myQueryName); return false; - } else { - methodParamsTemp.add(Constants.PARAM_QUERY); } + methodParamsTemp.add(Constants.PARAM_QUERY); } else { ourLog.trace("Query name does not match {}", myQueryName); return false; @@ -346,9 +345,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { if (theId == null || !theId.hasIdPart()) { String msg = theContext.getLocalizer().getMessage(SearchMethodBinding.class.getName() + ".idNullForCompartmentSearch"); throw new InvalidRequestException(msg); - } else { - compartmentSearch = true; } + compartmentSearch = true; } /* diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java index 2de5ad8bccc..698d4a2ead7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java @@ -283,8 +283,10 @@ public class SearchParameter extends BaseQueryParameter { this.myRequired = required; } - @SuppressWarnings({ "unchecked", "unused" }) + @SuppressWarnings("unchecked") public void setType(FhirContext theContext, final Class type, Class> theInnerCollectionType, Class> theOuterCollectionType) { + + this.myType = type; if (IQueryParameterType.class.isAssignableFrom(type)) { myParamBinder = new QueryParameterTypeBinder((Class) type, myCompositeTypes); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java index 1cad5634ac0..c7fbf04daf6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java @@ -1,55 +1,17 @@ package ca.uhn.fhir.rest.method; -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2017 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; import java.util.Set; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.IBaseResource; - -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.annotation.Since; -import ca.uhn.fhir.rest.param.ParameterUtil; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; - class SinceOrAtParameter extends SearchParameter { - private Class myType; - private String myParamName; - private Class myAnnotationType; +// private Class myType; +// private String myParamName; +// private Class myAnnotationType; public SinceOrAtParameter(String theParamName, Class theAnnotationType) { super(theParamName, false); - myParamName = theParamName; - myAnnotationType = theAnnotationType; +// myParamName = theParamName; +// myAnnotationType = theAnnotationType; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java index e82561dcc16..f9d2fc57a81 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java @@ -112,16 +112,15 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding if (theArgs[myTransactionParamIndex] instanceof Bundle) { Bundle bundle = (Bundle) theArgs[myTransactionParamIndex]; return createTransactionInvocation(bundle, context); - } else { - @SuppressWarnings("unchecked") - List resources = (List) theArgs[myTransactionParamIndex]; - return createTransactionInvocation(resources, context); } + @SuppressWarnings("unchecked") + List resources = (List) theArgs[myTransactionParamIndex]; + return createTransactionInvocation(resources, context); } @SuppressWarnings("unchecked") @Override - public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { + public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { /* * The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a 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 ae6c2da85ed..c7e7df4d787 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 @@ -30,7 +30,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Update; @@ -150,11 +149,10 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe if (isBlank(theResourceId)) { String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "noIdInBodyForUpdate"); throw new InvalidRequestException(msg); - } else { - if (!theResourceId.equals(theUrlId)) { - String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "incorrectIdForUpdate", theResourceId, theUrlId); - throw new InvalidRequestException(msg); - } + } + if (!theResourceId.equals(theUrlId)) { + String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBindingWithResourceParam.class, "incorrectIdForUpdate", theResourceId, theUrlId); + throw new InvalidRequestException(msg); } } else { theResource.setId((IIdType)null); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java index 2c2289a10c5..fe0fde123ad 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java @@ -14,7 +14,7 @@ import ca.uhn.fhir.context.FhirContext; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -48,7 +48,7 @@ abstract class BaseParam implements IQueryParameterType { public Boolean getMissing() { return myMissing; } - + @Override public final String getQueryParameterQualifier() { if (myMissing != null && myMissing.booleanValue()) { @@ -73,8 +73,8 @@ abstract class BaseParam implements IQueryParameterType { } /** - * If set to non-null value, indicates that this parameter has been populated - * with a "[name]:missing=true" or "[name]:missing=false" vale instead of a + * If set to non-null value, indicates that this parameter has been populated + * with a "[name]:missing=true" or "[name]:missing=false" vale instead of a * normal value * * @return Returns a reference to this for easier method chaining @@ -97,10 +97,10 @@ abstract class BaseParam implements IQueryParameterType { throw new InvalidRequestException(msg); } } - + myMissing = null; doSetValueAsQueryToken(theContext, theParamName, theQualifier, theValue); } } - + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java index d60480c8f1c..ea1fec5cbe9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java @@ -151,9 +151,8 @@ public abstract class BaseQueryParameter implements IParameter { if (handlesMissing()) { return parse(theRequest.getServer().getFhirContext(), paramList); - } else { - return null; } + return null; } return parse(theRequest.getServer().getFhirContext(), paramList); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java index 387a539d866..ba863284f64 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java @@ -32,7 +32,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IQueryParameterOr; -import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.primitive.DateDt; @@ -43,7 +42,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ValidateUtil; @SuppressWarnings("deprecation") -public class DateParam extends BaseParamWithPrefix implements IQueryParameterType , IQueryParameterOr { +public class DateParam extends BaseParamWithPrefix implements /*IQueryParameterType , */IQueryParameterOr { private final DateTimeDt myValue = new DateTimeDt(); @@ -220,9 +219,8 @@ public class DateParam extends BaseParamWithPrefix implements IQueryP public String getValueAsString() { if (myValue != null) { return myValue.getValueAsString(); - } else { - return null; } + return null; } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java index 9af925c56ff..b8003cc9f24 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java @@ -345,6 +345,7 @@ public class DateRangeParam implements IQueryParameterAnd { public void setRangeFromDatesInclusive(String theLowerBound, String theUpperBound) { myLowerBound = theLowerBound != null ? new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null; myUpperBound = theUpperBound != null ? new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null; + //FIXME potential null access on theLowerBound if (isNotBlank(theLowerBound) && isNotBlank(theUpperBound) && theLowerBound.equals(theUpperBound)) { myLowerBound.setPrefix(ParamPrefixEnum.EQUAL); myUpperBound.setPrefix(ParamPrefixEnum.EQUAL); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/InternalCodingDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/InternalCodingDt.java index 4495b778c99..e11c8e63200 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/InternalCodingDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/InternalCodingDt.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.util.CoverageIgnore; @CoverageIgnore public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype { + private static final long serialVersionUID = 993056016725918652L; + /** * Constructor */ @@ -78,6 +80,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype return super.isBaseEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(mySystem, myVersion, myCode, myDisplay, myPrimary); } + @Deprecated //override deprecated method @Override public List getAllPopulatedChildElementsOfType(Class theType) { return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType, mySystem, myVersion, myCode, myDisplay, myPrimary); @@ -90,6 +93,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype * Definition: The identification of the code system that defines the meaning of the symbol in the code. *

*/ + @Override public UriDt getSystemElement() { if (mySystem == null) { mySystem = new UriDt(); @@ -116,6 +120,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype * Definition: The identification of the code system that defines the meaning of the symbol in the code. *

*/ + @Override public InternalCodingDt setSystem(String theUri) { mySystem = new UriDt(theUri); return this; @@ -169,6 +174,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype * Definition: A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post-coordination) *

*/ + @Override public CodeDt getCodeElement() { if (myCode == null) { myCode = new CodeDt(); @@ -195,6 +201,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype * Definition: A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post-coordination) *

*/ + @Override public InternalCodingDt setCode(String theCode) { myCode = new CodeDt(theCode); return this; @@ -233,6 +240,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype * Definition: A representation of the meaning of the code in the system, following the rules of the system. *

*/ + @Override public InternalCodingDt setDisplay(String theString) { myDisplay = new StringDt(theString); return this; @@ -292,11 +300,13 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype return getDisplay(); } + @Deprecated //override deprecated method @Override public Boolean getMissing() { throw new UnsupportedOperationException(); } + @Deprecated //override deprecated method @Override public IQueryParameterType setMissing(Boolean theMissing) { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java index 82bc481f862..52593680d70 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java @@ -147,9 +147,8 @@ public enum ParamPrefixEnum { public String getValueForContext(FhirContext theContext) { if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { return getDstu1Value(); - } else { - return getValue(); } + return getValue(); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java index 1b63cfc9d2e..6dfcc942126 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java @@ -139,9 +139,8 @@ public class ParameterUtil { public static String escapeWithDefault(Object theValue) { if (theValue == null) { return ""; - } else { - return escape(theValue.toString()); } + return escape(theValue.toString()); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java index 150809c0806..cd29240777e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java @@ -29,11 +29,10 @@ import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.util.CoverageIgnore; -public class ReferenceParam extends BaseParam implements IQueryParameterType { +public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ { private String myChain; @@ -93,9 +92,8 @@ public class ReferenceParam extends BaseParam implements IQueryParameterType { String doGetValueAsQueryToken(FhirContext theContext) { if (isBlank(myId.getResourceType())) { return myId.getValue(); // e.g. urn:asdjd or 123 or cid:wieiuru or #1 - } else { - return myId.getIdPart(); } + return myId.getIdPart(); } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java index be68f9a766a..7ab06b5f59e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java @@ -150,6 +150,7 @@ public class ResourceParameter implements IParameter { String ctValue = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE); if (ctValue != null) { if (ctValue.startsWith("application/x-www-form-urlencoded")) { + //FIXME potential null access theMethodBinding String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType()); throw new InvalidRequestException(msg); } @@ -169,9 +170,8 @@ public class ResourceParameter implements IParameter { if (encoding == null) { String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType); throw new InvalidRequestException(msg); - } else { - requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents()), charset); } + requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents()), charset); } else { String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, restOperationType); throw new InvalidRequestException(msg); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java index 868b521bf3e..17e4730a39e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param; import static org.apache.commons.lang3.StringUtils.defaultString; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -32,17 +33,26 @@ import ca.uhn.fhir.rest.server.Constants; public class StringParam extends BaseParam implements IQueryParameterType { + private boolean myContains; private boolean myExact; private String myValue; - private boolean myContains; - + + /** + * Constructor + */ public StringParam() { } + /** + * Constructor + */ public StringParam(String theValue) { setValue(theValue); } + /** + * Constructor + */ public StringParam(String theValue, boolean theExact) { setValue(theValue); setExact(theExact); @@ -79,6 +89,29 @@ public class StringParam extends BaseParam implements IQueryParameterType { myValue = ParameterUtil.unescape(theValue); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof StringParam)) { + return false; + } + + StringParam other = (StringParam) obj; + + EqualsBuilder eb = new EqualsBuilder(); + eb.append(myExact, other.myExact); + eb.append(myContains, other.myContains); + eb.append(myValue, other.myValue); + eb.append(getMissing(), other.getMissing()); + + return eb.isEquals(); + } + public String getValue() { return myValue; } @@ -91,6 +124,13 @@ public class StringParam extends BaseParam implements IQueryParameterType { return defaultString(myValue); } + /** + * String parameter modifier :contains + */ + public boolean isContains() { + return myContains; + } + public boolean isEmpty() { return StringUtils.isEmpty(myValue); } @@ -99,6 +139,18 @@ public class StringParam extends BaseParam implements IQueryParameterType { return myExact; } + /** + * String parameter modifier :contains + */ + public StringParam setContains(boolean theContains) { + myContains = theContains; + if (myContains) { + setExact(false); + setMissing(null); + } + return this; + } + public StringParam setExact(boolean theExact) { myExact = theExact; if (myExact) { @@ -108,13 +160,6 @@ public class StringParam extends BaseParam implements IQueryParameterType { return this; } - /** - * String parameter modifier :contains - */ - public boolean isContains() { - return myContains; - } - public StringParam setValue(String theValue) { myValue = theValue; return this; @@ -136,16 +181,4 @@ public class StringParam extends BaseParam implements IQueryParameterType { return builder.toString(); } - /** - * String parameter modifier :contains - */ - public StringParam setContains(boolean theContains) { - myContains = theContains; - if (myContains) { - setExact(false); - setMissing(null); - } - return this; - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java index 5ddf6d101dc..0298c6bf7d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java @@ -27,12 +27,11 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.primitive.UriDt; -public class TokenParam extends BaseParam implements IQueryParameterType { +public class TokenParam extends BaseParam /*implements IQueryParameterType*/ { private TokenParamModifier myModifier; private String mySystem; @@ -86,9 +85,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType { String doGetQueryParameterQualifier() { if (getModifier() != null) { return getModifier().getValue(); - } else { - return null; } + return null; } /** @@ -98,9 +96,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType { String doGetValueAsQueryToken(FhirContext theContext) { if (getSystem() != null) { return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue()); - } else { - return ParameterUtil.escape(getValue()); } + return ParameterUtil.escape(getValue()); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java index 4c63992765a..ed2e9020584 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java @@ -48,7 +48,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class TransactionParameter implements IParameter { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionParameter.class); +// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionParameter.class); private FhirContext myContext; private ParamStyle myParamStyle; private Class myResourceBundleType; @@ -112,6 +112,7 @@ public class TransactionParameter implements IParameter { case DSTU1_BUNDLE: { Bundle bundle; bundle = parser.parseBundle(reader); + //FIXME resource leak return bundle; } case RESOURCE_LIST: { @@ -122,12 +123,15 @@ public class TransactionParameter implements IParameter { resourceList.add(next.getResource()); } } + //FIXME resource leak return resourceList; } case RESOURCE_BUNDLE: + //FIXME resource leak return parser.parseResource(myResourceBundleType, reader); } + //FIXME resource leak throw new IllegalStateException("Unknown type: " + myParamStyle); // should not happen } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java new file mode 100644 index 00000000000..d91c97129d6 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.rest.server; + +import org.apache.commons.lang3.Validate; + +public abstract class BasePagingProvider implements IPagingProvider { + + private int myDefaultPageSize = 10; + private int myMaximumPageSize = 50; + + public BasePagingProvider() { + super(); + } + + @Override + public int getDefaultPageSize() { + return myDefaultPageSize; + } + + @Override + public int getMaximumPageSize() { + return myMaximumPageSize; + } + + public BasePagingProvider setDefaultPageSize(int theDefaultPageSize) { + Validate.isTrue(theDefaultPageSize > 0, "size must be greater than 0"); + myDefaultPageSize = theDefaultPageSize; + return this; + } + + public BasePagingProvider setMaximumPageSize(int theMaximumPageSize) { + Validate.isTrue(theMaximumPageSize > 0, "size must be greater than 0"); + myMaximumPageSize = theMaximumPageSize; + return this; + } + +} \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java index 7f5b6bf462b..32027e57bde 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java @@ -64,6 +64,11 @@ public class BundleProviders { public Integer preferredPageSize() { return null; } + + @Override + public String getUuid() { + return null; + } }; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/FifoMemoryPagingProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/FifoMemoryPagingProvider.java index b33612f15e1..08dbda0b015 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/FifoMemoryPagingProvider.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/FifoMemoryPagingProvider.java @@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -25,11 +25,9 @@ import java.util.UUID; import org.apache.commons.lang3.Validate; -public class FifoMemoryPagingProvider implements IPagingProvider { +public class FifoMemoryPagingProvider extends BasePagingProvider implements IPagingProvider { private LinkedHashMap myBundleProviders; - private int myDefaultPageSize=10; - private int myMaximumPageSize=50; private int mySize; public FifoMemoryPagingProvider(int theSize) { @@ -39,33 +37,11 @@ public class FifoMemoryPagingProvider implements IPagingProvider { myBundleProviders = new LinkedHashMap(mySize); } - @Override - public int getDefaultPageSize() { - return myDefaultPageSize; - } - - @Override - public int getMaximumPageSize() { - return myMaximumPageSize; - } - @Override public synchronized IBundleProvider retrieveResultList(String theId) { return myBundleProviders.get(theId); } - public FifoMemoryPagingProvider setDefaultPageSize(int theDefaultPageSize) { - Validate.isTrue(theDefaultPageSize > 0, "size must be greater than 0"); - myDefaultPageSize = theDefaultPageSize; - return this; - } - - public FifoMemoryPagingProvider setMaximumPageSize(int theMaximumPageSize) { - Validate.isTrue(theMaximumPageSize > 0, "size must be greater than 0"); - myMaximumPageSize = theMaximumPageSize; - return this; - } - @Override public synchronized String storeResultList(IBundleProvider theList) { while (myBundleProviders.size() > mySize) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IBundleProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IBundleProvider.java index c833a3b0cba..71c7eae09fb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IBundleProvider.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IBundleProvider.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.server; +import java.util.Date; + /* * #%L * HAPI FHIR - Core Library @@ -10,7 +12,7 @@ package ca.uhn.fhir.rest.server; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -23,6 +25,7 @@ package ca.uhn.fhir.rest.server; import java.util.List; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.model.primitive.InstantDt; @@ -31,15 +34,17 @@ public interface IBundleProvider { /** * Load the given collection of resources by index, plus any additional resources per the * server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example, - * if the method is invoked with index 0,10 the method might return 10 search results, plus an + * if the method is invoked with index 0,10 the method might return 10 search results, plus an * additional 20 resources which matched a client's _include specification. * - * @param theFromIndex The low index (inclusive) to return - * @param theToIndex The high index (exclusive) to return + * @param theFromIndex + * The low index (inclusive) to return + * @param theToIndex + * The high index (exclusive) to return * @return A list of resources. The size of this list must be at least theToIndex - theFromIndex. */ List getResources(int theFromIndex, int theToIndex); - + /** * Optionally may be used to signal a preferred page size to the server, e.g. because * the implementing code recognizes that the resources which will be returned by this @@ -50,16 +55,31 @@ public interface IBundleProvider { * @return Returns the preferred page size or null */ Integer preferredPageSize(); - + /** * Returns the total number of results which match the given query (exclusive of any * _include's or OperationOutcome) */ int size(); - + /** * Returns the instant as of which this result was valid */ - InstantDt getPublished(); + IPrimitiveType getPublished(); + + /** + * Returns the UUID associated with this search. Note that this + * does not need to return a non-null value unless it a + * {@link IPagingProvider} is being used that requires UUIDs + * being returned. + *

+ * In other words, if you are using the default {@link FifoMemoryPagingProvider} in + * your server, it is fine for this method to simply return {@code null} since {@link FifoMemoryPagingProvider} + * does not use the value anyhow. On the other hand, if you are creating a custom + * [@code IPagingProvider} implementation you might use this method to communicate + * the search ID back to the provider. + *

+ */ + public String getUuid(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IResourceProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IResourceProvider.java index 8d5db9d99e5..35fbf8b240e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IResourceProvider.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IResourceProvider.java @@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulServer.java index 7d67796f9ed..46877d82d43 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IRestfulServer.java @@ -1,9 +1,6 @@ package ca.uhn.fhir.rest.server; -import java.util.List; - import ca.uhn.fhir.rest.method.RequestDetails; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; /* * #%L diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java index 0c3bf7a5e14..2e13855c823 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java @@ -55,9 +55,8 @@ public class ResourceBinding { if (rm.incomingServerRequestMatchesMethod(theRequest)) { ourLog.debug("Handler {} matches", rm); return rm; - } else { - ourLog.trace("Handler {} does not match", rm); } + ourLog.trace("Handler {} does not match", rm); } return null; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index d673656cd24..f1c0a5ff2a7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -266,9 +266,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer> allowableParams = foundMethodBinding.getAllowableParamAnnotations(); - if (allowableParams != null) { - for (Annotation[] nextParamAnnotations : m.getParameterAnnotations()) { - for (Annotation annotation : nextParamAnnotations) { - Package pack = annotation.annotationType().getPackage(); - if (pack.equals(IdParam.class.getPackage())) { - if (!allowableParams.contains(annotation.annotationType())) { - throw new ConfigurationException("Method[" + m.toString() + "] is not allowed to have a parameter annotated with " + annotation); - } - } + List> allowableParams = foundMethodBinding.getAllowableParamAnnotations(); + if (allowableParams != null) { + for (Annotation[] nextParamAnnotations : m.getParameterAnnotations()) { + for (Annotation annotation : nextParamAnnotations) { + Package pack = annotation.annotationType().getPackage(); + if (pack.equals(IdParam.class.getPackage())) { + if (!allowableParams.contains(annotation.annotationType())) { + throw new ConfigurationException("Method[" + m.toString() + "] is not allowed to have a parameter annotated with " + annotation); } } } - - resourceBinding.addMethod(foundMethodBinding); - ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName()); } } + + resourceBinding.addMethod(foundMethodBinding); + ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName()); + } return count; @@ -406,6 +404,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theType) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java index 0a4a93c772b..c05a5b432ba 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java @@ -42,7 +42,6 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; -import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index 1ff01053224..e5e4ca59f49 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -33,7 +33,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.io.Charsets; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.text.StrLookup; @@ -47,7 +46,6 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; -import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -259,9 +257,8 @@ public class LoggingInterceptor extends InterceptorAdapter { default: return ""; } - } else { - return ""; } + return ""; } else if ("id".equals(theKey)) { if (myRequestDetails.getId() != null) { return myRequestDetails.getId().getValue(); @@ -305,9 +302,8 @@ public class LoggingInterceptor extends InterceptorAdapter { ResponseEncoding encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequestDetails, myRequestDetails.getServer().getDefaultResponseEncoding()); if (encoding != null) { return encoding.getEncoding().name(); - } else { - return ""; } + return ""; } else if (theKey.equals("exceptionMessage")) { return myException != null ? myException.getMessage() : null; } else if (theKey.equals("requestUrl")) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index 2f1b53eeb03..dd26ab8dc06 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -44,7 +44,6 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; -import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.CoverageIgnore; /** @@ -209,9 +208,8 @@ public class AuthorizationInterceptor extends InterceptorAdapter implements ISer if (decision.getDecidingRule() != null) { String ruleName = defaultString(decision.getDecidingRule().getName(), "(unnamed rule)"); throw new ForbiddenOperationException("Access denied by rule: " + ruleName); - } else { - throw new ForbiddenOperationException("Access denied by default policy (no applicable rules)"); } + throw new ForbiddenOperationException("Access denied by default policy (no applicable rules)"); } private void handleUserOperation(RequestDetails theRequest, IBaseResource theResource, RestOperationTypeEnum operation) { @@ -230,7 +228,7 @@ public class AuthorizationInterceptor extends InterceptorAdapter implements ISer inputResourceId = theProcessedRequest.getId(); break; case OUT: - inputResource = null; +// inputResource = null; inputResourceId = theProcessedRequest.getId(); break; case NONE: diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderOperationNamed.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderOperationNamed.java index be8f3d1ff0b..4d1e4d07e95 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderOperationNamed.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderOperationNamed.java @@ -1,7 +1,5 @@ package ca.uhn.fhir.rest.server.interceptor.auth; -import org.apache.http.impl.client.HttpClientBuilder; - /* * #%L * HAPI FHIR - Core Library diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleConditionalClassifier.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleConditionalClassifier.java index ffaf2dca76e..4b8a8449ccd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleConditionalClassifier.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleConditionalClassifier.java @@ -1,7 +1,5 @@ package ca.uhn.fhir.rest.server.interceptor.auth; -import java.util.List; - /* * #%L * HAPI FHIR - Core Library diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java index 89eb25d8127..118e9b08c80 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java @@ -41,7 +41,7 @@ import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.BundleUtil.BundleEntryParts; import ca.uhn.fhir.util.FhirTerser; -class RuleImplOp extends BaseRule implements IAuthRule { +class RuleImplOp extends BaseRule /*implements IAuthRule*/ { private AppliesTypeEnum myAppliesTo; private Set myAppliesToTypes; @@ -108,9 +108,8 @@ class RuleImplOp extends BaseRule implements IAuthRule { if (theOperation == RestOperationTypeEnum.DELETE) { if (theInputResource == null) { return newVerdict(); - } else { - appliesToResource = theInputResource; } + appliesToResource = theInputResource; } else { return null; } @@ -123,45 +122,44 @@ class RuleImplOp extends BaseRule implements IAuthRule { if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) { if (getMode() == PolicyEnum.DENY) { return new Verdict(PolicyEnum.DENY, this); - } else { - List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); - Verdict verdict = null; - for (BundleEntryParts nextPart : inputResources) { - - IBaseResource inputResource = nextPart.getResource(); - RestOperationTypeEnum operation = null; - if (nextPart.getRequestType() == RequestTypeEnum.GET) { - continue; - } - if (nextPart.getRequestType() == RequestTypeEnum.POST) { - operation = RestOperationTypeEnum.CREATE; - } else if (nextPart.getRequestType() == RequestTypeEnum.PUT) { - operation = RestOperationTypeEnum.UPDATE; - } else { - throw new InvalidRequestException("Can not handle transaction with operation of type " + nextPart.getRequestType()); - } - - /* - * This is basically just being conservative - Be careful of transactions containing - * nested operations and nested transactions. We block the by default. At some point - * it would be nice to be more nuanced here. - */ - RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource()); - if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { - throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); - } - - Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null, null); - if (newVerdict == null) { - continue; - } else if (verdict == null) { - verdict = newVerdict; - } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { - verdict = newVerdict; - } - } - return verdict; } + List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); + Verdict verdict = null; + for (BundleEntryParts nextPart : inputResources) { + + IBaseResource inputResource = nextPart.getResource(); + RestOperationTypeEnum operation = null; + if (nextPart.getRequestType() == RequestTypeEnum.GET) { + continue; + } + if (nextPart.getRequestType() == RequestTypeEnum.POST) { + operation = RestOperationTypeEnum.CREATE; + } else if (nextPart.getRequestType() == RequestTypeEnum.PUT) { + operation = RestOperationTypeEnum.UPDATE; + } else { + throw new InvalidRequestException("Can not handle transaction with operation of type " + nextPart.getRequestType()); + } + + /* + * This is basically just being conservative - Be careful of transactions containing + * nested operations and nested transactions. We block the by default. At some point + * it would be nice to be more nuanced here. + */ + RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource()); + if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { + throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); + } + + Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null, null); + if (newVerdict == null) { + continue; + } else if (verdict == null) { + verdict = newVerdict; + } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { + verdict = newVerdict; + } + } + return verdict; } else if (theOutputResource != null) { List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); Verdict verdict = null; @@ -189,9 +187,8 @@ class RuleImplOp extends BaseRule implements IAuthRule { case METADATA: if (theOperation == RestOperationTypeEnum.METADATA) { return newVerdict(); - } else { - return null; } + return null; default: // Should not happen throw new IllegalStateException("Unable to apply security to event of type " + theOperation); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index 3000ed48133..03ab3d077f0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -105,7 +105,7 @@ public class ServletRequestDetails extends RequestDetails { } } } - + //FIXME resource leak return requestContents; } catch (IOException e) { ourLog.error("Could not load request resource", e); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java index 457825bc52e..64108021fee 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java @@ -51,12 +51,11 @@ public class ServletRestfulResponse extends RestfulResponse void addElement(ArrayList retVal, IElement next, Class theType) { + //FIXME There seems to be an error on theType == null => if (theType != null|| theType.isAssignableFrom if (theType == null|| theType.isAssignableFrom(next.getClass())) { - retVal.add((T) next); + retVal.add(theType.cast(next)); } if (next instanceof ICompositeElement) { ICompositeElement iCompositeElement = (ICompositeElement) next; + //TODO: Use of a deprecated method should be resolved. retVal.addAll(iCompositeElement.getAllPopulatedChildElementsOfType(theType)); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index a35a062a319..37f86b4fa7e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -70,13 +70,11 @@ public class FhirTerser { if (theTarget instanceof IPrimitiveType) { ((IPrimitiveType)theTarget).setValueAsString(((IPrimitiveType)theSource).getValueAsString()); return; - } else { - if (theIgnoreMissingFields) { - return; - } else { - throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName()); - } } + if (theIgnoreMissingFields) { + return; + } + throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName()); } BaseRuntimeElementCompositeDefinition sourceDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(theSource.getClass()); @@ -94,9 +92,8 @@ public class FhirTerser { if (targetChild == null) { if (theIgnoreMissingFields) { continue; - } else { - throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + elementName); } + throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + elementName); } BaseRuntimeElementDefinition childDef = targetChild.getChildByName(elementName); @@ -166,10 +163,9 @@ public class FhirTerser { if (theSubList.size() == 1) { return nextDef; - } else { - BaseRuntimeElementCompositeDefinition cmp = (BaseRuntimeElementCompositeDefinition) nextDef.getChildByName(theSubList.get(0)); - return getDefinition(cmp, theSubList.subList(1, theSubList.size())); } + BaseRuntimeElementCompositeDefinition cmp = (BaseRuntimeElementCompositeDefinition) nextDef.getChildByName(theSubList.get(0)); + return getDefinition(cmp, theSubList.subList(1, theSubList.size())); } public BaseRuntimeChildDefinition getDefinition(Class theResourceType, String thePath) { @@ -208,9 +204,8 @@ public class FhirTerser { List retVal = getValues(currentDef, currentObj, parts, theWantedType); if (retVal.isEmpty()) { return null; - } else { - return retVal.get(0); } + return retVal.get(0); } @SuppressWarnings("unchecked") diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java index 20edcd94db8..d1e22dd61c9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java @@ -27,8 +27,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; /** * @see FhirTerser#visit(IBaseResource, IModelVisitor) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java index 29900f74fb2..07238019a2f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java @@ -90,9 +90,8 @@ public class OperationOutcomeUtil { public static String getFirstIssueDetails(FhirContext theCtx, IBaseOperationOutcome theOutcome) { if (theCtx.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { return getFirstIssueStringPart(theCtx, theOutcome, "diagnostics"); - } else { - return getFirstIssueStringPart(theCtx, theOutcome, "details"); } + return getFirstIssueStringPart(theCtx, theOutcome, "details"); } public static String getFirstIssueLocation(FhirContext theCtx, IBaseOperationOutcome theOutcome) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java index 217e6e9ab66..620e36dbbc4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java @@ -47,6 +47,7 @@ public class PortUtil { Thread.sleep(500); return port; } catch (Exception e) { + //FIXME resource leak throw new Error(e); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java index 6b842387f40..0c3c991628c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java @@ -81,9 +81,8 @@ public class ReflectionUtil { ParameterizedType pt = ((ParameterizedType) firstArg); Type pt2 = pt.getActualTypeArguments()[0]; return (Class) pt2; - } else { - type = (Class) firstArg; } + type = (Class) firstArg; return type; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java index ccdd40f8089..d3bf689217f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java @@ -107,9 +107,8 @@ public class ResourceReferenceInfo { } } return false; - } else { - // DSTU1 style - return (theInclude.getValue().equals(myOwningResource + '.' + myName)); } + // DSTU1 style + return (theInclude.getValue().equals(myOwningResource + '.' + myName)); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index 81c850905e5..a275a36a640 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -244,45 +244,45 @@ public class UrlUtil { retVal.setResourceType(id.getResourceType()); retVal.setResourceId(id.getIdPart()); retVal.setVersionId(id.getVersionIdPart()); - return retVal; - } else { - if (url.matches("\\/[a-zA-Z]+\\?.*")) { - url = url.substring(1); - } - int nextStart = 0; - boolean nextIsHistory = false; - - for (int idx = 0; idx < url.length(); idx++) { - char nextChar = url.charAt(idx); - boolean atEnd = (idx + 1) == url.length(); - if (nextChar == '?' || nextChar == '/' || atEnd) { - int endIdx = (atEnd && nextChar != '?') ? idx + 1 : idx; - String nextSubstring = url.substring(nextStart, endIdx); - if (retVal.getResourceType() == null) { - retVal.setResourceType(nextSubstring); - } else if (retVal.getResourceId() == null) { - retVal.setResourceId(nextSubstring); - } else if (nextIsHistory) { - retVal.setVersionId(nextSubstring); - } else { - if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) { - nextIsHistory = true; - } else { - throw new InvalidRequestException("Invalid FHIR resource URL: " + url); - } - } - if (nextChar == '?') { - if (url.length() > idx + 1) { - retVal.setParams(url.substring(idx + 1, url.length())); - } - break; - } - nextStart = idx + 1; - } - } - return retVal; } + if (url.matches("\\/[a-zA-Z]+\\?.*")) { + url = url.substring(1); + } + int nextStart = 0; + boolean nextIsHistory = false; + + for (int idx = 0; idx < url.length(); idx++) { + char nextChar = url.charAt(idx); + boolean atEnd = (idx + 1) == url.length(); + if (nextChar == '?' || nextChar == '/' || atEnd) { + int endIdx = (atEnd && nextChar != '?') ? idx + 1 : idx; + String nextSubstring = url.substring(nextStart, endIdx); + if (retVal.getResourceType() == null) { + retVal.setResourceType(nextSubstring); + } else if (retVal.getResourceId() == null) { + retVal.setResourceId(nextSubstring); + } else if (nextIsHistory) { + retVal.setVersionId(nextSubstring); + } else { + if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) { + nextIsHistory = true; + } else { + throw new InvalidRequestException("Invalid FHIR resource URL: " + url); + } + } + if (nextChar == '?') { + if (url.length() > idx + 1) { + retVal.setParams(url.substring(idx + 1, url.length())); + } + break; + } + nextStart = idx + 1; + } + } + + return retVal; + } public static String unescape(String theString) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java index 72370ef455b..c38ee9e5e7f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java @@ -1694,9 +1694,8 @@ public class XmlUtil { if (ourNextException != null) { if (ourNextException instanceof FactoryConfigurationError) { throw ((FactoryConfigurationError)ourNextException); - } else { - throw (XMLStreamException)ourNextException; } + throw (XMLStreamException)ourNextException; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java index ec96225c20a..21ea4fb3eda 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java @@ -100,8 +100,7 @@ public class DependencyLogImpl implements IDependencyLog { } catch (MalformedURLException ex) { return null; } - } else { - return null; } + return null; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java index 9961c19d4bd..61a7f790b5e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java @@ -165,7 +165,7 @@ public class SchemaBaseValidator implements IValidatorModule { baseIs = new BOMInputStream(baseIs, false); InputStreamReader baseReader = new InputStreamReader(baseIs, Charset.forName("UTF-8")); Source baseSource = new StreamSource(baseReader, theSystemId); - + //FIXME resource leak return baseSource; } @@ -240,7 +240,7 @@ public class SchemaBaseValidator implements IValidatorModule { } input.setByteStream(baseIs); - + //FIXME resource leak return input; } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/DefinitionException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/DefinitionException.java index 0e3aee9c2eb..3896b1dd9b1 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/DefinitionException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/DefinitionException.java @@ -23,6 +23,8 @@ package org.hl7.fhir.exceptions; public class DefinitionException extends FHIRException { + private static final long serialVersionUID = 8175803009974854088L; + public DefinitionException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/FHIRException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/FHIRException.java index 0bcb6cb7f37..274864c3078 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/FHIRException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/FHIRException.java @@ -25,6 +25,8 @@ public class FHIRException extends Exception { // Note that the 4-argument constructor has been removed as it is not JDK6 compatible + private static final long serialVersionUID = -1793365096090608037L; + public FHIRException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/NoTerminologyServiceException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/NoTerminologyServiceException.java index cf01c90072f..08be82d5d89 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/NoTerminologyServiceException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/NoTerminologyServiceException.java @@ -23,6 +23,8 @@ package org.hl7.fhir.exceptions; public class NoTerminologyServiceException extends FHIRException { + private static final long serialVersionUID = 8196224236579018584L; + public NoTerminologyServiceException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java index 3dc96c5e294..b56c9d9212c 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/PathEngineException.java @@ -23,6 +23,8 @@ package org.hl7.fhir.exceptions; public class PathEngineException extends FHIRException { + private static final long serialVersionUID = -8550234068917355391L; + public PathEngineException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/TerminologyServiceException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/TerminologyServiceException.java index 5760798d4fa..2f1ae3dca5a 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/TerminologyServiceException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/TerminologyServiceException.java @@ -23,6 +23,8 @@ package org.hl7.fhir.exceptions; public class TerminologyServiceException extends FHIRException { + private static final long serialVersionUID = -1547510329455394225L; + public TerminologyServiceException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/UcumException.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/UcumException.java index 081b3ac8274..c1c2bd8a90e 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/UcumException.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/exceptions/UcumException.java @@ -23,6 +23,8 @@ package org.hl7.fhir.exceptions; public class UcumException extends FHIRException { + private static final long serialVersionUID = -8535757334881835619L; + public UcumException() { super(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java index 119ddb66e4e..35af39be3a5 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java @@ -64,6 +64,7 @@ public interface IIdType extends IPrimitiveType { * * @see #getIdPart() */ + @Override String getValue(); String getVersionIdPart(); @@ -93,6 +94,7 @@ public interface IIdType extends IPrimitiveType { */ boolean isAbsolute(); + @Override boolean isEmpty(); /** diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/INarrative.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/INarrative.java index 2d0a86454d1..400155c72b9 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/INarrative.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/INarrative.java @@ -23,6 +23,7 @@ package org.hl7.fhir.instance.model.api; public interface INarrative extends ICompositeType { + @Override boolean isEmpty(); // TODO: use less broad exception type here diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/MyURIResolver.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/MyURIResolver.java index ffe257f8d88..bea4891ccd4 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/MyURIResolver.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/MyURIResolver.java @@ -48,7 +48,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + */ import java.io.File; import java.io.FileInputStream; @@ -63,33 +63,34 @@ import javax.xml.transform.stream.StreamSource; public class MyURIResolver implements URIResolver { - private String path; - private URIResolver alt; - - public MyURIResolver(String path, URIResolver alt) { - super(); - this.path = path; - this.alt = alt; - } + private String path; + private URIResolver alt; + + public MyURIResolver(String path, URIResolver alt) { + super(); + this.path = path; + this.alt = alt; + } - @Override - public Source resolve(String href, String base) throws TransformerException { - try { - if (href.startsWith("http://") || href.startsWith("https://")) { - if (alt != null) { - Source s = alt.resolve(href, base); - if (s != null) - return s; - } - return TransformerFactory.newInstance().getURIResolver().resolve(href, base); - } else - return new StreamSource(new FileInputStream(href.contains(File.separator) ? href : Utilities.path(path, href))); - } catch (FileNotFoundException e) { - throw new TransformerException(e); - } catch (IOException e) { - throw new TransformerException(e); - } - } + @Override + public Source resolve(String href, String base) throws TransformerException { + try { + if (href.startsWith("http://") || href.startsWith("https://")) { + if (alt != null) { + Source s = alt.resolve(href, base); + if (s != null) + return s; + } + return TransformerFactory.newInstance().getURIResolver().resolve(href, base); + } else + //FIXME resource leak + return new StreamSource(new FileInputStream(href.contains(File.separator) ? href : Utilities.path(path, href))); + } catch (FileNotFoundException e) { + throw new TransformerException(e); + } catch (IOException e) { + throw new TransformerException(e); + } + } } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Table.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Table.java index dfca0f8dd5e..881ffc7f9f2 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Table.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Table.java @@ -26,12 +26,12 @@ import java.util.List; public class Table { - private int rowCount; +// private int rowCount; private int colCount; private List> rows = new ArrayList>(); public Table(int rowCount, int colCount) { - this.rowCount = rowCount; +// this.rowCount = rowCount; this.colCount = colCount; for (int r = 0; r < rowCount; r++) { rows.add(new ArrayList()); diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/TextFile.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/TextFile.java index 740a18f33cb..5ec46b917cc 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/TextFile.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/TextFile.java @@ -25,7 +25,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + */ package org.hl7.fhir.utilities; /* @@ -64,17 +64,17 @@ public class TextFile { public static List readAllLines(String path) throws IOException { List result = new ArrayList(); - + File file = new CSFile(path); BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8")); - + while( reader.ready() ) result.add(reader.readLine()); - + reader.close(); return result; } - + public static void writeAllLines(String path, List lines) throws IOException { File file = new CSFile(path); @@ -82,53 +82,54 @@ public class TextFile { OutputStreamWriter sw = new OutputStreamWriter(s, "UTF-8"); for( String line : lines ) sw.write(line + "\r\n"); - + sw.flush(); s.close(); } - - - public static void stringToFile(String content, String path) throws IOException { - File file = new CSFile(path); - OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); - sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter - sw.write(content); - sw.flush(); - sw.close(); - } - public static void stringToFileNoPrefix(String content, String path) throws IOException { - File file = new CSFile(path); - OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); - sw.write(content); - sw.flush(); - sw.close(); - } - public static String fileToString(String src) throws FileNotFoundException, IOException { - return streamToString(new FileInputStream(new CSFile(src))); + public static void stringToFile(String content, String path) throws IOException { + File file = new CSFile(path); + OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); + sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter + sw.write(content); + sw.flush(); + sw.close(); } - public static String streamToString(InputStream input) throws IOException { - InputStreamReader sr = new InputStreamReader(input, "UTF-8"); - StringBuilder b = new StringBuilder(); - //while (sr.ready()) { Commented out by Claude Nanjo (1/14/2014) - sr.ready() always returns false - please remove if change does not impact other areas of codebase - int i = -1; - while((i = sr.read()) > -1) { - char c = (char) i; - b.append(c); - } -// sr.close(); - - return b.toString().replace("\uFEFF", ""); - } + public static void stringToFileNoPrefix(String content, String path) throws IOException { + File file = new CSFile(path); + OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); + sw.write(content); + sw.flush(); + sw.close(); + } - public static void bytesToFile(byte[] bytes, String path) throws IOException { - File file = new CSFile(path); - OutputStream sw = new FileOutputStream(file); - sw.write(bytes); - sw.flush(); - sw.close(); - - } + public static String fileToString(String src) throws FileNotFoundException, IOException { + //FIXME resource leak + return streamToString(new FileInputStream(new CSFile(src))); + } + + public static String streamToString(InputStream input) throws IOException { + InputStreamReader sr = new InputStreamReader(input, "UTF-8"); + StringBuilder b = new StringBuilder(); + //while (sr.ready()) { Commented out by Claude Nanjo (1/14/2014) - sr.ready() always returns false - please remove if change does not impact other areas of codebase + int i = -1; + while((i = sr.read()) > -1) { + char c = (char) i; + b.append(c); + } + // sr.close(); + + return b.toString().replace("\uFEFF", ""); + } + + public static void bytesToFile(byte[] bytes, String path) throws IOException { + File file = new CSFile(path); + OutputStream sw = new FileOutputStream(file); + sw.write(bytes); + sw.flush(); + sw.close(); + + } } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Utilities.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Utilities.java index e5504da10a7..45facbdd195 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -28,976 +28,926 @@ POSSIBILITY OF SUCH DAMAGE. */ package org.hl7.fhir.utilities; -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2017 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.channels.FileChannel; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.SourceDataLine; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.URIResolver; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.apache.commons.io.FileUtils; -import org.hl7.fhir.exceptions.FHIRException; - -import net.sf.saxon.TransformerFactoryImpl; public class Utilities { -// private static final String TOKEN_REGEX = "^a-z[A-Za-z0-9]*$"; + // private static final String TOKEN_REGEX = "^a-z[A-Za-z0-9]*$"; + + + // private static final String OID_REGEX = "[0-2](\\.(0|[1-9]([0-9])*))*"; + // + // + // + public static boolean isInteger(String string) { + try { + int i = Integer.parseInt(string); + return i != i+1; + } catch (Exception e) { + return false; + } + } + + public static boolean isHex(String string) { + try { + int i = Integer.parseInt(string, 16); + return i != i+1; + } catch (Exception e) { + return false; + } + } + + public static boolean isDecimal(String string) { + if (Utilities.noString(string)) + return false; + try { + float r = Float.parseFloat(string); + return r != r + 1; // just to suppress the hint + } catch (Exception e) { + return false; + } + } -// private static final String OID_REGEX = "[0-2](\\.(0|[1-9]([0-9])*))*"; -// -// -// - public static boolean isInteger(String string) { - try { - int i = Integer.parseInt(string); - return i != i+1; - } catch (Exception e) { - return false; - } - } - - public static boolean isHex(String string) { - try { - int i = Integer.parseInt(string, 16); - return i != i+1; - } catch (Exception e) { - return false; - } - } - - public static boolean isDecimal(String string) { - if (Utilities.noString(string)) - return false; - try { - float r = Float.parseFloat(string); - return r != r + 1; // just to suppress the hint - } catch (Exception e) { - return false; - } - } - - public static String escapeXml(String doco) { if (doco == null) return ""; - + StringBuilder b = new StringBuilder(); for (char c : doco.toCharArray()) { - if (c == '<') - b.append("<"); - else if (c == '>') - b.append(">"); - else if (c == '&') - b.append("&"); - else if (c == '"') - b.append("""); - else - b.append(c); + if (c == '<') + b.append("<"); + else if (c == '>') + b.append(">"); + else if (c == '&') + b.append("&"); + else if (c == '"') + b.append("""); + else + b.append(c); } return b.toString(); } -// -// + // + // public static String capitalize(String s) { if( s == null ) return null; if( s.length() == 0 ) return s; if( s.length() == 1 ) return s.toUpperCase(); - + return s.substring(0, 1).toUpperCase() + s.substring(1); } -// -// public static void copyDirectory(String sourceFolder, String destFolder, FileNotifier notifier) throws IOException, FHIRException { -// CSFile src = new CSFile(sourceFolder); -// if (!src.exists()) -// throw new FHIRException("Folder " +sourceFolder+" not found"); -// createDirectory(destFolder); -// -// String[] files = src.list(); -// for (String f : files) { -// if (new CSFile(sourceFolder+File.separator+f).isDirectory()) { -// if (!f.startsWith(".")) // ignore .svn... -// copyDirectory(sourceFolder+File.separator+f, destFolder+File.separator+f, notifier); -// } else { -// if (notifier != null) -// notifier.copyFile(sourceFolder+File.separator+f, destFolder+File.separator+f); -// copyFile(new CSFile(sourceFolder+File.separator+f), new CSFile(destFolder+File.separator+f)); -// } -// } -// } -// -// public static void copyFile(String source, String dest) throws IOException { -// copyFile(new File(source), new File(dest)); -// } -// -// public static void copyFile(File sourceFile, File destFile) throws IOException { -// if(!destFile.exists()) { -// if (!new CSFile(destFile.getParent()).exists()) { -// createDirectory(destFile.getParent()); -// } -// destFile.createNewFile(); -// } -// -// FileChannel source = null; -// FileChannel destination = null; -// -// try { -// source = new FileInputStream(sourceFile).getChannel(); -// destination = new FileOutputStream(destFile).getChannel(); -// destination.transferFrom(source, 0, source.size()); -// } -// finally { -// if(source != null) { -// source.close(); -// } -// if(destination != null) { -// destination.close(); -// } -// } -// } -// -// public static boolean checkFolder(String dir, List errors) -// throws IOException -// { -// if (!new CSFile(dir).exists()) { -// errors.add("Unable to find directory "+dir); -// return false; -// } else { -// return true; -// } -// } -// -// public static boolean checkFile(String purpose, String dir, String file, List errors) -// throws IOException -// { -// if (!new CSFile(dir+file).exists()) { -// errors.add("Unable to find "+purpose+" file "+file+" in "+dir); -// return false; -// } else { -// return true; -// } -// } -// -// public static String asCSV(List strings) { -// StringBuilder s = new StringBuilder(); -// boolean first = true; -// for (String n : strings) { -// if (!first) -// s.append(","); -// s.append(n); -// first = false; -// } -// return s.toString(); -// } -// -// public static String asHtmlBr(String prefix, List strings) { -// StringBuilder s = new StringBuilder(); -// boolean first = true; -// for (String n : strings) { -// if (!first) -// s.append("
"); -// s.append(prefix); -// s.append(n); -// first = false; -// } -// return s.toString(); -// } -// -// public static void clearDirectory(String folder) throws IOException { -// File dir = new File(folder); -// if (dir.exists()) -// FileUtils.cleanDirectory(dir); -//// String[] files = new CSFile(folder).list(); -//// if (files != null) { -//// for (String f : files) { -//// File fh = new CSFile(folder+File.separatorChar+f); -//// if (fh.isDirectory()) -//// clearDirectory(fh.getAbsolutePath()); -//// fh.delete(); -//// } -//// } -// } -// -// public static void createDirectory(String path) throws IOException{ -// new CSFile(path).mkdirs(); -// } -// -// public static String changeFileExt(String name, String ext) { -// if (name.lastIndexOf('.') > -1) -// return name.substring(0, name.lastIndexOf('.')) + ext; -// else -// return name+ext; -// } -// -// public static String cleanupTextString( String contents ) -// { -// if( contents == null || contents.trim().equals("") ) -// return null; -// else -// return contents.trim(); -// } -// -// - public static boolean noString(String v) { - return v == null || v.equals(""); - } -// -// -// public static byte[] saxonTransform(Map files, byte[] source, byte[] xslt) throws TransformerException { -// TransformerFactory f = new net.sf.saxon.TransformerFactoryImpl(); -// f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); -// StreamSource xsrc = new StreamSource(new ByteArrayInputStream(xslt)); -// f.setURIResolver(new ZipURIResolver(files)); -// Transformer t = f.newTransformer(xsrc); -// -// t.setURIResolver(new ZipURIResolver(files)); -// StreamSource src = new StreamSource(new ByteArrayInputStream(source)); -// ByteArrayOutputStream out = new ByteArrayOutputStream(); -// StreamResult res = new StreamResult(out); -// t.transform(src, res); -// return out.toByteArray(); -// } -// -// public static byte[] transform(Map files, byte[] source, byte[] xslt) throws TransformerException { -// TransformerFactory f = TransformerFactory.newInstance(); -// f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); -// StreamSource xsrc = new StreamSource(new ByteArrayInputStream(xslt)); -// f.setURIResolver(new ZipURIResolver(files)); -// Transformer t = f.newTransformer(xsrc); -// -// t.setURIResolver(new ZipURIResolver(files)); -// StreamSource src = new StreamSource(new ByteArrayInputStream(source)); -// ByteArrayOutputStream out = new ByteArrayOutputStream(); -// StreamResult res = new StreamResult(out); -// t.transform(src, res); -// return out.toByteArray(); -// } -// -// public static void bytesToFile(byte[] content, String filename) throws IOException { -// FileOutputStream out = new FileOutputStream(filename); -// out.write(content); -// out.close(); -// -// } -// -// public static String saxonTransform(String source, String xslt) throws TransformerException, FileNotFoundException { -// TransformerFactoryImpl f = new net.sf.saxon.TransformerFactoryImpl(); -// f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); -// StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); -// Transformer t = f.newTransformer(xsrc); -// StreamSource src = new StreamSource(new FileInputStream(source)); -// StreamResult res = new StreamResult(new ByteArrayOutputStream()); -// t.transform(src, res); -// return res.getOutputStream().toString(); -// } -// -// public static void saxonTransform(String xsltDir, String source, String xslt, String dest, URIResolver alt) throws FileNotFoundException, TransformerException { -// saxonTransform(xsltDir, source, xslt, dest, alt, null); -// } -// -// public static void saxonTransform(String xsltDir, String source, String xslt, String dest, URIResolver alt, Map params) throws FileNotFoundException, TransformerException { -// TransformerFactoryImpl f = new net.sf.saxon.TransformerFactoryImpl(); -// f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); -// StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); -// f.setURIResolver(new MyURIResolver(xsltDir, alt)); -// Transformer t = f.newTransformer(xsrc); -// if (params != null) { -// for (Map.Entry entry : params.entrySet()) { -// t.setParameter(entry.getKey(), entry.getValue()); -// } -// } -// -// t.setURIResolver(new MyURIResolver(xsltDir, alt)); -// StreamSource src = new StreamSource(new FileInputStream(source)); -// StreamResult res = new StreamResult(new FileOutputStream(dest)); -// t.transform(src, res); -// } -// -// public static void transform(String xsltDir, String source, String xslt, String dest, URIResolver alt) throws FileNotFoundException, TransformerException { -// -// TransformerFactory f = TransformerFactory.newInstance(); -// StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); -// f.setURIResolver(new MyURIResolver(xsltDir, alt)); -// Transformer t = f.newTransformer(xsrc); -// -// t.setURIResolver(new MyURIResolver(xsltDir, alt)); -// StreamSource src = new StreamSource(new FileInputStream(source)); -// StreamResult res = new StreamResult(new FileOutputStream(dest)); -// t.transform(src, res); -// -// } -// -// - public static String appendSlash(String definitions) { - return definitions.endsWith(File.separator) ? definitions : definitions+File.separator; - } -// -// public static String appendForwardSlash(String definitions) { -// return definitions.endsWith("/") ? definitions : definitions+"/"; -// } -// -// -// public static String fileTitle(String file) { -// if (file == null) -// return null; -// String s = new File(file).getName(); -// return s.indexOf(".") == -1? s : s.substring(0, s.indexOf(".")); -// } -// -// -// public static String systemEol() -// { -// return System.getProperty("line.separator"); -// } -// - public static String normaliseEolns(String value) { - return value.replace("\r\n", "\r").replace("\n", "\r").replace("\r", "\r\n"); - } -// -// -// public static String unescapeXml(String xml) throws FHIRException { -// if (xml == null) -// return null; -// -// StringBuilder b = new StringBuilder(); -// int i = 0; -// while (i < xml.length()) { -// if (xml.charAt(i) == '&') { -// StringBuilder e = new StringBuilder(); -// i++; -// while (xml.charAt(i) != ';') { -// e.append(xml.charAt(i)); -// i++; -// } -// if (e.toString().equals("lt")) -// b.append("<"); -// else if (e.toString().equals("gt")) -// b.append(">"); -// else if (e.toString().equals("amp")) -// b.append("&"); -// else if (e.toString().equals("quot")) -// b.append("\""); -// else if (e.toString().equals("mu")) -// b.append((char)956); -// else -// throw new FHIRException("unknown XML entity \""+e.toString()+"\""); -// } else -// b.append(xml.charAt(i)); -// i++; -// } -// return b.toString(); -// } -// -// -// -// -// public static String padRight(String src, char c, int len) { -// StringBuilder s = new StringBuilder(); -// s.append(src); -// for (int i = 0; i < len - src.length(); i++) -// s.append(c); -// return s.toString(); -// } -// -// - public static String padLeft(String src, char c, int len) { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < len - src.length(); i++) - s.append(c); - s.append(src); - return s.toString(); - } -// -// - public static String path(String... args) throws IOException { - StringBuilder s = new StringBuilder(); - boolean d = false; - for(String arg: args) { - if (!d) - d = !noString(arg); - else if (!s.toString().endsWith(File.separator)) - s.append(File.separator); - String a = arg; - a = a.replace("\\", File.separator); - if (s.length() > 0 && a.startsWith(File.separator)) - a = a.substring(File.separator.length()); - - while (a.startsWith(".."+File.separator)) { - String p = s.toString().substring(0, s.length()-1); - if (!p.contains(File.separator)) - throw new IOException("illegal path underrun "+Arrays.asList(args)); - s = new StringBuilder(p.substring(0, p.lastIndexOf(File.separator))+File.separator); - a = a.substring(3); - } - if ("..".equals(a)) { - int i = s.substring(0, s.length()-1).lastIndexOf(File.separator); - s = new StringBuilder(s.substring(0, i+1)); - } else - s.append(a); - } - return s.toString(); - } + // + // public static void copyDirectory(String sourceFolder, String destFolder, FileNotifier notifier) throws IOException, FHIRException { + // CSFile src = new CSFile(sourceFolder); + // if (!src.exists()) + // throw new FHIRException("Folder " +sourceFolder+" not found"); + // createDirectory(destFolder); + // + // String[] files = src.list(); + // for (String f : files) { + // if (new CSFile(sourceFolder+File.separator+f).isDirectory()) { + // if (!f.startsWith(".")) // ignore .svn... + // copyDirectory(sourceFolder+File.separator+f, destFolder+File.separator+f, notifier); + // } else { + // if (notifier != null) + // notifier.copyFile(sourceFolder+File.separator+f, destFolder+File.separator+f); + // copyFile(new CSFile(sourceFolder+File.separator+f), new CSFile(destFolder+File.separator+f)); + // } + // } + // } + // + // public static void copyFile(String source, String dest) throws IOException { + // copyFile(new File(source), new File(dest)); + // } + // + // public static void copyFile(File sourceFile, File destFile) throws IOException { + // if(!destFile.exists()) { + // if (!new CSFile(destFile.getParent()).exists()) { + // createDirectory(destFile.getParent()); + // } + // destFile.createNewFile(); + // } + // + // FileChannel source = null; + // FileChannel destination = null; + // + // try { + // source = new FileInputStream(sourceFile).getChannel(); + // destination = new FileOutputStream(destFile).getChannel(); + // destination.transferFrom(source, 0, source.size()); + // } + // finally { + // if(source != null) { + // source.close(); + // } + // if(destination != null) { + // destination.close(); + // } + // } + // } + // + // public static boolean checkFolder(String dir, List errors) + // throws IOException + // { + // if (!new CSFile(dir).exists()) { + // errors.add("Unable to find directory "+dir); + // return false; + // } else { + // return true; + // } + // } + // + // public static boolean checkFile(String purpose, String dir, String file, List errors) + // throws IOException + // { + // if (!new CSFile(dir+file).exists()) { + // errors.add("Unable to find "+purpose+" file "+file+" in "+dir); + // return false; + // } else { + // return true; + // } + // } + // + // public static String asCSV(List strings) { + // StringBuilder s = new StringBuilder(); + // boolean first = true; + // for (String n : strings) { + // if (!first) + // s.append(","); + // s.append(n); + // first = false; + // } + // return s.toString(); + // } + // + // public static String asHtmlBr(String prefix, List strings) { + // StringBuilder s = new StringBuilder(); + // boolean first = true; + // for (String n : strings) { + // if (!first) + // s.append("
"); + // s.append(prefix); + // s.append(n); + // first = false; + // } + // return s.toString(); + // } + // + // public static void clearDirectory(String folder) throws IOException { + // File dir = new File(folder); + // if (dir.exists()) + // FileUtils.cleanDirectory(dir); + //// String[] files = new CSFile(folder).list(); + //// if (files != null) { + //// for (String f : files) { + //// File fh = new CSFile(folder+File.separatorChar+f); + //// if (fh.isDirectory()) + //// clearDirectory(fh.getAbsolutePath()); + //// fh.delete(); + //// } + //// } + // } + // + // public static void createDirectory(String path) throws IOException{ + // new CSFile(path).mkdirs(); + // } + // + // public static String changeFileExt(String name, String ext) { + // if (name.lastIndexOf('.') > -1) + // return name.substring(0, name.lastIndexOf('.')) + ext; + // else + // return name+ext; + // } + // + // public static String cleanupTextString( String contents ) + // { + // if( contents == null || contents.trim().equals("") ) + // return null; + // else + // return contents.trim(); + // } + // + // + public static boolean noString(String v) { + return v == null || v.equals(""); + } + // + // + // public static byte[] saxonTransform(Map files, byte[] source, byte[] xslt) throws TransformerException { + // TransformerFactory f = new net.sf.saxon.TransformerFactoryImpl(); + // f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); + // StreamSource xsrc = new StreamSource(new ByteArrayInputStream(xslt)); + // f.setURIResolver(new ZipURIResolver(files)); + // Transformer t = f.newTransformer(xsrc); + // + // t.setURIResolver(new ZipURIResolver(files)); + // StreamSource src = new StreamSource(new ByteArrayInputStream(source)); + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // StreamResult res = new StreamResult(out); + // t.transform(src, res); + // return out.toByteArray(); + // } + // + // public static byte[] transform(Map files, byte[] source, byte[] xslt) throws TransformerException { + // TransformerFactory f = TransformerFactory.newInstance(); + // f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); + // StreamSource xsrc = new StreamSource(new ByteArrayInputStream(xslt)); + // f.setURIResolver(new ZipURIResolver(files)); + // Transformer t = f.newTransformer(xsrc); + // + // t.setURIResolver(new ZipURIResolver(files)); + // StreamSource src = new StreamSource(new ByteArrayInputStream(source)); + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // StreamResult res = new StreamResult(out); + // t.transform(src, res); + // return out.toByteArray(); + // } + // + // public static void bytesToFile(byte[] content, String filename) throws IOException { + // FileOutputStream out = new FileOutputStream(filename); + // out.write(content); + // out.close(); + // + // } + // + // public static String saxonTransform(String source, String xslt) throws TransformerException, FileNotFoundException { + // TransformerFactoryImpl f = new net.sf.saxon.TransformerFactoryImpl(); + // f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); + // StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); + // Transformer t = f.newTransformer(xsrc); + // StreamSource src = new StreamSource(new FileInputStream(source)); + // StreamResult res = new StreamResult(new ByteArrayOutputStream()); + // t.transform(src, res); + // return res.getOutputStream().toString(); + // } + // + // public static void saxonTransform(String xsltDir, String source, String xslt, String dest, URIResolver alt) throws FileNotFoundException, TransformerException { + // saxonTransform(xsltDir, source, xslt, dest, alt, null); + // } + // + // public static void saxonTransform(String xsltDir, String source, String xslt, String dest, URIResolver alt, Map params) throws FileNotFoundException, TransformerException { + // TransformerFactoryImpl f = new net.sf.saxon.TransformerFactoryImpl(); + // f.setAttribute("http://saxon.sf.net/feature/version-warning", Boolean.FALSE); + // StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); + // f.setURIResolver(new MyURIResolver(xsltDir, alt)); + // Transformer t = f.newTransformer(xsrc); + // if (params != null) { + // for (Map.Entry entry : params.entrySet()) { + // t.setParameter(entry.getKey(), entry.getValue()); + // } + // } + // + // t.setURIResolver(new MyURIResolver(xsltDir, alt)); + // StreamSource src = new StreamSource(new FileInputStream(source)); + // StreamResult res = new StreamResult(new FileOutputStream(dest)); + // t.transform(src, res); + // } + // + // public static void transform(String xsltDir, String source, String xslt, String dest, URIResolver alt) throws FileNotFoundException, TransformerException { + // + // TransformerFactory f = TransformerFactory.newInstance(); + // StreamSource xsrc = new StreamSource(new FileInputStream(xslt)); + // f.setURIResolver(new MyURIResolver(xsltDir, alt)); + // Transformer t = f.newTransformer(xsrc); + // + // t.setURIResolver(new MyURIResolver(xsltDir, alt)); + // StreamSource src = new StreamSource(new FileInputStream(source)); + // StreamResult res = new StreamResult(new FileOutputStream(dest)); + // t.transform(src, res); + // + // } + // + // + public static String appendSlash(String definitions) { + return definitions.endsWith(File.separator) ? definitions : definitions+File.separator; + } + // + // public static String appendForwardSlash(String definitions) { + // return definitions.endsWith("/") ? definitions : definitions+"/"; + // } + // + // + // public static String fileTitle(String file) { + // if (file == null) + // return null; + // String s = new File(file).getName(); + // return s.indexOf(".") == -1? s : s.substring(0, s.indexOf(".")); + // } + // + // + // public static String systemEol() + // { + // return System.getProperty("line.separator"); + // } + // + public static String normaliseEolns(String value) { + return value.replace("\r\n", "\r").replace("\n", "\r").replace("\r", "\r\n"); + } + // + // + // public static String unescapeXml(String xml) throws FHIRException { + // if (xml == null) + // return null; + // + // StringBuilder b = new StringBuilder(); + // int i = 0; + // while (i < xml.length()) { + // if (xml.charAt(i) == '&') { + // StringBuilder e = new StringBuilder(); + // i++; + // while (xml.charAt(i) != ';') { + // e.append(xml.charAt(i)); + // i++; + // } + // if (e.toString().equals("lt")) + // b.append("<"); + // else if (e.toString().equals("gt")) + // b.append(">"); + // else if (e.toString().equals("amp")) + // b.append("&"); + // else if (e.toString().equals("quot")) + // b.append("\""); + // else if (e.toString().equals("mu")) + // b.append((char)956); + // else + // throw new FHIRException("unknown XML entity \""+e.toString()+"\""); + // } else + // b.append(xml.charAt(i)); + // i++; + // } + // return b.toString(); + // } + // + // + // + // + // public static String padRight(String src, char c, int len) { + // StringBuilder s = new StringBuilder(); + // s.append(src); + // for (int i = 0; i < len - src.length(); i++) + // s.append(c); + // return s.toString(); + // } + // + // + public static String padLeft(String src, char c, int len) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < len - src.length(); i++) + s.append(c); + s.append(src); + return s.toString(); + } + // + // + public static String path(String... args) throws IOException { + StringBuilder s = new StringBuilder(); + boolean d = false; + for(String arg: args) { + if (!d) + d = !noString(arg); + else if (!s.toString().endsWith(File.separator)) + s.append(File.separator); + String a = arg; + a = a.replace("\\", File.separator); + if (s.length() > 0 && a.startsWith(File.separator)) + a = a.substring(File.separator.length()); - public static String pathReverse(String... args) { - StringBuilder s = new StringBuilder(); - boolean d = false; - for(String arg: args) { - if (!d) - d = !noString(arg); - else if (!s.toString().endsWith("/")) - s.append("/"); - s.append(arg); - } - return s.toString(); - } -// -// -// -//// public static void checkCase(String filename) { -//// File f = new CSFile(filename); -//// if (!f.getName().equals(filename)) -//// throw new FHIRException("Filename ") -//// -//// } -// -// public static String nmtokenize(String cs) { -// StringBuilder s = new StringBuilder(); -// for (int i = 0; i < cs.length(); i++) { -// char c = cs.charAt(i); -// if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_') -// s.append(c); -// else if (c != ' ') -// s.append("."+Integer.toString(c)); -// } -// return s.toString(); -// } -// -// -// public static boolean isToken(String tail) { -// if (tail == null || tail.length() == 0) -// return false; -// boolean result = isAlphabetic(tail.charAt(0)); -// for (int i = 1; i < tail.length(); i++) { -// result = result && (isAlphabetic(tail.charAt(i)) || isDigit(tail.charAt(i)) || (tail.charAt(i) == '_') || (tail.charAt(i) == '[') || (tail.charAt(i) == ']')); -// } -// return result; -// } -// -// -// private static boolean isDigit(char c) { -// return (c >= '0') && (c <= '9'); -// } -// -// -// private static boolean isAlphabetic(char c) { -// return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); -// } -// -// -// public static String getDirectoryForFile(String filepath) { -// File f = new File(filepath); -// return f.getParent(); -// } -// -// public static String appendPeriod(String s) { -// if (Utilities.noString(s)) -// return s; -// s = s.trim(); -// if (s.endsWith(".") || s.endsWith("?")) -// return s; -// return s+"."; -// } -// -// -// public static String removePeriod(String s) { -// if (Utilities.noString(s)) -// return s; -// if (s.endsWith(".")) -// return s.substring(0, s.length()-1); -// return s; -// } -// -// -// public static String stripBOM(String string) { -// return string.replace("\uFEFF", ""); -// } -// -// -// public static String oidTail(String id) { -// if (id == null || !id.contains(".")) -// return id; -// return id.substring(id.lastIndexOf(".")+1); -// } -// -// -// public static String oidRoot(String id) { -// if (id == null || !id.contains(".")) -// return id; -// return id.substring(0, id.indexOf(".")); -// } -// - public static String escapeJava(String doco) { - if (doco == null) - return ""; - - StringBuilder b = new StringBuilder(); - for (char c : doco.toCharArray()) { - if (c == '\r') - b.append("\\r"); - else if (c == '\n') - b.append("\\n"); - else if (c == '"') - b.append("\\\""); - else if (c == '\\') - b.append("\\\\"); - else - b.append(c); - } - return b.toString(); - } -// -// -// public static String[] splitByCamelCase(String name) { -// List parts = new ArrayList(); -// StringBuilder b = new StringBuilder(); -// for (int i = 0; i < name.length(); i++) { -// if (i > 0 && Character.isUpperCase(name.charAt(i))) { -// parts.add(b.toString()); -// b = new StringBuilder(); -// } -// b.append(Character.toLowerCase(name.charAt(i))); -// } -// parts.add(b.toString()); -// return parts.toArray(new String[] {} ); -// } -// -// -// public static String encodeUri(String v) { -// return v.replace(" ", "%20").replace("?", "%3F").replace("=", "%3D"); -// } -// -// -// + while (a.startsWith(".."+File.separator)) { + String p = s.toString().substring(0, s.length()-1); + if (!p.contains(File.separator)) + throw new IOException("illegal path underrun "+Arrays.asList(args)); + s = new StringBuilder(p.substring(0, p.lastIndexOf(File.separator))+File.separator); + a = a.substring(3); + } + if ("..".equals(a)) { + int i = s.substring(0, s.length()-1).lastIndexOf(File.separator); + s = new StringBuilder(s.substring(0, i+1)); + } else + s.append(a); + } + return s.toString(); + } + + public static String pathReverse(String... args) { + StringBuilder s = new StringBuilder(); + boolean d = false; + for(String arg: args) { + if (!d) + d = !noString(arg); + else if (!s.toString().endsWith("/")) + s.append("/"); + s.append(arg); + } + return s.toString(); + } + // + // + // + //// public static void checkCase(String filename) { + //// File f = new CSFile(filename); + //// if (!f.getName().equals(filename)) + //// throw new FHIRException("Filename ") + //// + //// } + // + // public static String nmtokenize(String cs) { + // StringBuilder s = new StringBuilder(); + // for (int i = 0; i < cs.length(); i++) { + // char c = cs.charAt(i); + // if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_') + // s.append(c); + // else if (c != ' ') + // s.append("."+Integer.toString(c)); + // } + // return s.toString(); + // } + // + // + // public static boolean isToken(String tail) { + // if (tail == null || tail.length() == 0) + // return false; + // boolean result = isAlphabetic(tail.charAt(0)); + // for (int i = 1; i < tail.length(); i++) { + // result = result && (isAlphabetic(tail.charAt(i)) || isDigit(tail.charAt(i)) || (tail.charAt(i) == '_') || (tail.charAt(i) == '[') || (tail.charAt(i) == ']')); + // } + // return result; + // } + // + // + // private static boolean isDigit(char c) { + // return (c >= '0') && (c <= '9'); + // } + // + // + // private static boolean isAlphabetic(char c) { + // return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); + // } + // + // + // public static String getDirectoryForFile(String filepath) { + // File f = new File(filepath); + // return f.getParent(); + // } + // + // public static String appendPeriod(String s) { + // if (Utilities.noString(s)) + // return s; + // s = s.trim(); + // if (s.endsWith(".") || s.endsWith("?")) + // return s; + // return s+"."; + // } + // + // + // public static String removePeriod(String s) { + // if (Utilities.noString(s)) + // return s; + // if (s.endsWith(".")) + // return s.substring(0, s.length()-1); + // return s; + // } + // + // + // public static String stripBOM(String string) { + // return string.replace("\uFEFF", ""); + // } + // + // + // public static String oidTail(String id) { + // if (id == null || !id.contains(".")) + // return id; + // return id.substring(id.lastIndexOf(".")+1); + // } + // + // + // public static String oidRoot(String id) { + // if (id == null || !id.contains(".")) + // return id; + // return id.substring(0, id.indexOf(".")); + // } + // + public static String escapeJava(String doco) { + if (doco == null) + return ""; + + StringBuilder b = new StringBuilder(); + for (char c : doco.toCharArray()) { + if (c == '\r') + b.append("\\r"); + else if (c == '\n') + b.append("\\n"); + else if (c == '"') + b.append("\\\""); + else if (c == '\\') + b.append("\\\\"); + else + b.append(c); + } + return b.toString(); + } + // + // + // public static String[] splitByCamelCase(String name) { + // List parts = new ArrayList(); + // StringBuilder b = new StringBuilder(); + // for (int i = 0; i < name.length(); i++) { + // if (i > 0 && Character.isUpperCase(name.charAt(i))) { + // parts.add(b.toString()); + // b = new StringBuilder(); + // } + // b.append(Character.toLowerCase(name.charAt(i))); + // } + // parts.add(b.toString()); + // return parts.toArray(new String[] {} ); + // } + // + // + // public static String encodeUri(String v) { + // return v.replace(" ", "%20").replace("?", "%3F").replace("=", "%3D"); + // } + // + // + // public static String normalize(String s) { if (noString(s)) return null; - StringBuilder b = new StringBuilder(); - boolean isWhitespace = false; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (!Character.isWhitespace(c)) { - b.append(Character.toLowerCase(c)); - isWhitespace = false; - } else if (!isWhitespace) { - b.append(' '); - isWhitespace = true; - } - } - return b.toString().trim(); - } -// -// public static String normalizeSameCase(String s) { -// if (noString(s)) -// return null; -// StringBuilder b = new StringBuilder(); -// boolean isWhitespace = false; -// for (int i = 0; i < s.length(); i++) { -// char c = s.charAt(i); -// if (!Character.isWhitespace(c)) { -// b.append(c); -// isWhitespace = false; -// } else if (!isWhitespace) { -// b.append(' '); -// isWhitespace = true; -// } -// } -// return b.toString().trim(); -// } -// -// -// public static void copyFileToDirectory(File source, File destDir) throws IOException { -// copyFile(source, new File(path(destDir.getAbsolutePath(), source.getName()))); -// } -// -// -// public static boolean isWhitespace(String s) { -// boolean ok = true; -// for (int i = 0; i < s.length(); i++) -// ok = ok && Character.isWhitespace(s.charAt(i)); -// return ok; -// -// } -// -// -// public static String URLEncode(String string) { -// try { -// return URLEncoder.encode(string, "UTF-8"); -// } catch (UnsupportedEncodingException e) { -// throw new Error(e.getMessage()); -// } -// } -// -// - public static boolean charInSet(char value, char... array) { - for (int i : array) - if (value == i) - return true; - return false; - } -// -// -// public static boolean charInRange(char ch, char a, char z) { -// return ch >= a && ch <= z; -// } -// - public static boolean existsInList(String value, String... array) { - if (value == null) - return false; - for (String s : array) - if (value.equals(s)) - return true; - return false; - } + StringBuilder b = new StringBuilder(); + boolean isWhitespace = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!Character.isWhitespace(c)) { + b.append(Character.toLowerCase(c)); + isWhitespace = false; + } else if (!isWhitespace) { + b.append(' '); + isWhitespace = true; + } + } + return b.toString().trim(); + } + // + // public static String normalizeSameCase(String s) { + // if (noString(s)) + // return null; + // StringBuilder b = new StringBuilder(); + // boolean isWhitespace = false; + // for (int i = 0; i < s.length(); i++) { + // char c = s.charAt(i); + // if (!Character.isWhitespace(c)) { + // b.append(c); + // isWhitespace = false; + // } else if (!isWhitespace) { + // b.append(' '); + // isWhitespace = true; + // } + // } + // return b.toString().trim(); + // } + // + // + // public static void copyFileToDirectory(File source, File destDir) throws IOException { + // copyFile(source, new File(path(destDir.getAbsolutePath(), source.getName()))); + // } + // + // + // public static boolean isWhitespace(String s) { + // boolean ok = true; + // for (int i = 0; i < s.length(); i++) + // ok = ok && Character.isWhitespace(s.charAt(i)); + // return ok; + // + // } + // + // + // public static String URLEncode(String string) { + // try { + // return URLEncoder.encode(string, "UTF-8"); + // } catch (UnsupportedEncodingException e) { + // throw new Error(e.getMessage()); + // } + // } + // + // + public static boolean charInSet(char value, char... array) { + for (int i : array) + if (value == i) + return true; + return false; + } + // + // + // public static boolean charInRange(char ch, char a, char z) { + // return ch >= a && ch <= z; + // } + // + public static boolean existsInList(String value, String... array) { + if (value == null) + return false; + for (String s : array) + if (value.equals(s)) + return true; + return false; + } - public static boolean existsInList(int value, int... array) { - for (int i : array) - if (value == i) - return true; - return false; - } + public static boolean existsInList(int value, int... array) { + for (int i : array) + if (value == i) + return true; + return false; + } - public static boolean existsInListNC(String value, String... array) { - for (String s : array) - if (value.equalsIgnoreCase(s)) - return true; - return false; - } -// -// -// public static String getFileNameForName(String name) { -// return name.toLowerCase(); -// } -// -// public static void deleteTempFiles() throws IOException { -// File file = createTempFile("test", "test"); -// String folder = getDirectoryForFile(file.getAbsolutePath()); -// String[] list = new File(folder).list(new FilenameFilter() { -// public boolean accept(File dir, String name) { -// return name.startsWith("ohfu-"); -// } -// }); -// if (list != null) { -// for (String n : list) { -// new File(path(folder, n)).delete(); -// } -// } -// } -// -// public static File createTempFile(String prefix, String suffix) throws IOException { -// // this allows use to eaily identify all our dtemp files and delete them, since delete on Exit doesn't really work. -// File file = File.createTempFile("ohfu-"+prefix, suffix); -// file.deleteOnExit(); -// return file; -// } -// -// -// public static boolean isAsciiChar(char ch) { -// return ch >= ' ' && ch <= '~'; -// } -// -// -// public static String makeUuidUrn() { -// return "urn:uuid:"+UUID.randomUUID().toString().toLowerCase(); -// } -// - public static boolean isURL(String s) { - boolean ok = s.matches("^http(s{0,1})://[a-zA-Z0-9_/\\-\\.]+\\.([A-Za-z/]{2,5})[a-zA-Z0-9_/\\&\\?\\=\\-\\.\\~\\%]*"); - return ok; - } -// -// -// public static String escapeJson(String value) { -// if (value == null) -// return ""; -// -// StringBuilder b = new StringBuilder(); -// for (char c : value.toCharArray()) { -// if (c == '\r') -// b.append("\\r"); -// else if (c == '\n') -// b.append("\\n"); -// else if (c == '"') -// b.append("\\\""); -// else if (c == '\'') -// b.append("\\'"); -// else if (c == '\\') -// b.append("\\\\"); -// else -// b.append(c); -// } -// return b.toString(); -// } -// -// public static String humanize(String code) { -// StringBuilder b = new StringBuilder(); -// boolean lastBreak = true; -// for (char c : code.toCharArray()) { -// if (Character.isLetter(c)) { -// if (lastBreak) -// b.append(Character.toUpperCase(c)); -// else { -// if (Character.isUpperCase(c)) -// b.append(" "); -// b.append(c); -// } -// lastBreak = false; -// } else { -// b.append(" "); -// lastBreak = true; -// } -// } -// if (b.length() == 0) -// return code; -// else -// return b.toString(); -// } -// -// - public static String uncapitalize(String s) { - if( s == null ) return null; - if( s.length() == 0 ) return s; - if( s.length() == 1 ) return s.toLowerCase(); - - return s.substring(0, 1).toLowerCase() + s.substring(1); - } + public static boolean existsInListNC(String value, String... array) { + for (String s : array) + if (value.equalsIgnoreCase(s)) + return true; + return false; + } + // + // + // public static String getFileNameForName(String name) { + // return name.toLowerCase(); + // } + // + // public static void deleteTempFiles() throws IOException { + // File file = createTempFile("test", "test"); + // String folder = getDirectoryForFile(file.getAbsolutePath()); + // String[] list = new File(folder).list(new FilenameFilter() { + // public boolean accept(File dir, String name) { + // return name.startsWith("ohfu-"); + // } + // }); + // if (list != null) { + // for (String n : list) { + // new File(path(folder, n)).delete(); + // } + // } + // } + // + // public static File createTempFile(String prefix, String suffix) throws IOException { + // // this allows use to eaily identify all our dtemp files and delete them, since delete on Exit doesn't really work. + // File file = File.createTempFile("ohfu-"+prefix, suffix); + // file.deleteOnExit(); + // return file; + // } + // + // + // public static boolean isAsciiChar(char ch) { + // return ch >= ' ' && ch <= '~'; + // } + // + // + // public static String makeUuidUrn() { + // return "urn:uuid:"+UUID.randomUUID().toString().toLowerCase(); + // } + // + public static boolean isURL(String s) { + boolean ok = s.matches("^http(s{0,1})://[a-zA-Z0-9_/\\-\\.]+\\.([A-Za-z/]{2,5})[a-zA-Z0-9_/\\&\\?\\=\\-\\.\\~\\%]*"); + return ok; + } + // + // + // public static String escapeJson(String value) { + // if (value == null) + // return ""; + // + // StringBuilder b = new StringBuilder(); + // for (char c : value.toCharArray()) { + // if (c == '\r') + // b.append("\\r"); + // else if (c == '\n') + // b.append("\\n"); + // else if (c == '"') + // b.append("\\\""); + // else if (c == '\'') + // b.append("\\'"); + // else if (c == '\\') + // b.append("\\\\"); + // else + // b.append(c); + // } + // return b.toString(); + // } + // + // public static String humanize(String code) { + // StringBuilder b = new StringBuilder(); + // boolean lastBreak = true; + // for (char c : code.toCharArray()) { + // if (Character.isLetter(c)) { + // if (lastBreak) + // b.append(Character.toUpperCase(c)); + // else { + // if (Character.isUpperCase(c)) + // b.append(" "); + // b.append(c); + // } + // lastBreak = false; + // } else { + // b.append(" "); + // lastBreak = true; + // } + // } + // if (b.length() == 0) + // return code; + // else + // return b.toString(); + // } + // + // + public static String uncapitalize(String s) { + if( s == null ) return null; + if( s.length() == 0 ) return s; + if( s.length() == 1 ) return s.toLowerCase(); + + return s.substring(0, 1).toLowerCase() + s.substring(1); + } - public static int charCount(String s, char c) { - int res = 0; - for (char ch : s.toCharArray()) - if (ch == c) - res++; - return res; - } + public static int charCount(String s, char c) { + int res = 0; + for (char ch : s.toCharArray()) + if (ch == c) + res++; + return res; + } -// -// // http://stackoverflow.com/questions/3780406/how-to-play-a-sound-alert-in-a-java-application -// public static float SAMPLE_RATE = 8000f; -// -// public static void tone(int hz, int msecs) { -// tone(hz, msecs, 1.0); -// } -// -// public static void tone(int hz, int msecs, double vol) { -// try { -// byte[] buf = new byte[1]; -// AudioFormat af = -// new AudioFormat( -// SAMPLE_RATE, // sampleRate -// 8, // sampleSizeInBits -// 1, // channels -// true, // signed -// false); // bigEndian -// SourceDataLine sdl; -// sdl = AudioSystem.getSourceDataLine(af); -// sdl.open(af); -// sdl.start(); -// for (int i=0; i < msecs*8; i++) { -// double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI; -// buf[0] = (byte)(Math.sin(angle) * 127.0 * vol); -// sdl.write(buf,0,1); -// } -// sdl.drain(); -// sdl.stop(); -// sdl.close(); -// } catch (Exception e) { -// } -// } -// -// -// public static boolean isOid(String cc) { -// return cc.matches(OID_REGEX) && cc.lastIndexOf('.') > 5; -// } -// -// -// public static boolean equals(String one, String two) { -// if (one == null && two == null) -// return true; -// if (one == null || two == null) -// return false; -// return one.equals(two); -// } -// -// -// public static void deleteAllFiles(String folder, String type) { -// File src = new File(folder); -// String[] files = src.list(); -// for (String f : files) { -// if (new File(folder+File.separator+f).isDirectory()) { -// deleteAllFiles(folder+File.separator+f, type); -// } else if (f.endsWith(type)) { -// new File(folder+File.separator+f).delete(); -// } -// } -// -// } -// -// public static boolean compareIgnoreWhitespace(File f1, File f2) throws IOException { -// InputStream in1 = null; -// InputStream in2 = null; -// try { -// in1 = new BufferedInputStream(new FileInputStream(f1)); -// in2 = new BufferedInputStream(new FileInputStream(f2)); -// -// int expectedByte = in1.read(); -// while (expectedByte != -1) { -// boolean w1 = isWhitespace(expectedByte); -// if (w1) -// while (isWhitespace(expectedByte)) -// expectedByte = in1.read(); -// int foundByte = in2.read(); -// if (w1) { -// if (!isWhitespace(foundByte)) -// return false; -// while (isWhitespace(foundByte)) -// foundByte = in2.read(); -// } -// if (expectedByte != foundByte) -// return false; -// expectedByte = in1.read(); -// } -// if (in2.read() != -1) { -// return false; -// } -// return true; -// } finally { -// if (in1 != null) { -// try { -// in1.close(); -// } catch (IOException e) {} -// } -// if (in2 != null) { -// try { -// in2.close(); -// } catch (IOException e) {} -// } -// } -// } -// -// private static boolean isWhitespace(int b) { -// return b == 9 || b == 10 || b == 13 || b == 32; -// } -// -// -// public static boolean compareIgnoreWhitespace(String fn1, String fn2) throws IOException { -// return compareIgnoreWhitespace(new File(fn1), new File(fn2)); -// } -// -// - public static boolean isAbsoluteUrl(String ref) { - return ref != null && ref.startsWith("http:") || ref.startsWith("https:") || ref.startsWith("urn:uuid:") || ref.startsWith("urn:oid:") ; - } + // + // // http://stackoverflow.com/questions/3780406/how-to-play-a-sound-alert-in-a-java-application + // public static float SAMPLE_RATE = 8000f; + // + // public static void tone(int hz, int msecs) { + // tone(hz, msecs, 1.0); + // } + // + // public static void tone(int hz, int msecs, double vol) { + // try { + // byte[] buf = new byte[1]; + // AudioFormat af = + // new AudioFormat( + // SAMPLE_RATE, // sampleRate + // 8, // sampleSizeInBits + // 1, // channels + // true, // signed + // false); // bigEndian + // SourceDataLine sdl; + // sdl = AudioSystem.getSourceDataLine(af); + // sdl.open(af); + // sdl.start(); + // for (int i=0; i < msecs*8; i++) { + // double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI; + // buf[0] = (byte)(Math.sin(angle) * 127.0 * vol); + // sdl.write(buf,0,1); + // } + // sdl.drain(); + // sdl.stop(); + // sdl.close(); + // } catch (Exception e) { + // } + // } + // + // + // public static boolean isOid(String cc) { + // return cc.matches(OID_REGEX) && cc.lastIndexOf('.') > 5; + // } + // + // + // public static boolean equals(String one, String two) { + // if (one == null && two == null) + // return true; + // if (one == null || two == null) + // return false; + // return one.equals(two); + // } + // + // + // public static void deleteAllFiles(String folder, String type) { + // File src = new File(folder); + // String[] files = src.list(); + // for (String f : files) { + // if (new File(folder+File.separator+f).isDirectory()) { + // deleteAllFiles(folder+File.separator+f, type); + // } else if (f.endsWith(type)) { + // new File(folder+File.separator+f).delete(); + // } + // } + // + // } + // + // public static boolean compareIgnoreWhitespace(File f1, File f2) throws IOException { + // InputStream in1 = null; + // InputStream in2 = null; + // try { + // in1 = new BufferedInputStream(new FileInputStream(f1)); + // in2 = new BufferedInputStream(new FileInputStream(f2)); + // + // int expectedByte = in1.read(); + // while (expectedByte != -1) { + // boolean w1 = isWhitespace(expectedByte); + // if (w1) + // while (isWhitespace(expectedByte)) + // expectedByte = in1.read(); + // int foundByte = in2.read(); + // if (w1) { + // if (!isWhitespace(foundByte)) + // return false; + // while (isWhitespace(foundByte)) + // foundByte = in2.read(); + // } + // if (expectedByte != foundByte) + // return false; + // expectedByte = in1.read(); + // } + // if (in2.read() != -1) { + // return false; + // } + // return true; + // } finally { + // if (in1 != null) { + // try { + // in1.close(); + // } catch (IOException e) {} + // } + // if (in2 != null) { + // try { + // in2.close(); + // } catch (IOException e) {} + // } + // } + // } + // + // private static boolean isWhitespace(int b) { + // return b == 9 || b == 10 || b == 13 || b == 32; + // } + // + // + // public static boolean compareIgnoreWhitespace(String fn1, String fn2) throws IOException { + // return compareIgnoreWhitespace(new File(fn1), new File(fn2)); + // } + // + // + public static boolean isAbsoluteUrl(String ref) { + //FIXME null access on ref cause by OR clause. + return ref != null && ref.startsWith("http:") || ref.startsWith("https:") || ref.startsWith("urn:uuid:") || ref.startsWith("urn:oid:") ; + } - public static boolean equivalent(String l, String r) { - if (Utilities.noString(l) && Utilities.noString(r)) - return true; - if (Utilities.noString(l) || Utilities.noString(r)) - return false; - return l.toLowerCase().equals(r.toLowerCase()); - } + public static boolean equivalent(String l, String r) { + if (Utilities.noString(l) && Utilities.noString(r)) + return true; + if (Utilities.noString(l) || Utilities.noString(r)) + return false; + return l.toLowerCase().equals(r.toLowerCase()); + } - public static boolean equivalentNumber(String l, String r) { - if (Utilities.noString(l) && Utilities.noString(r)) - return true; - if (Utilities.noString(l) || Utilities.noString(r)) - return false; - l = l.toLowerCase().trim(); - r = r.toLowerCase().trim(); // not that this should make any difference - return l.startsWith(r) || r.startsWith(l); - } -// -// -// public static String getFileExtension(String fn) { -// return fn.contains(".") ? fn.substring(fn.lastIndexOf(".")+1) : ""; -// } -// -// -// public static String unCamelCase(String name) { -// StringBuilder b = new StringBuilder(); -// boolean first = true; -// for (char c : name.toCharArray()) { -// if (Character.isUpperCase(c)) { -// if (!first) -// b.append(" "); -// b.append(Character.toLowerCase(c)); -// } else -// b.append(c); -// first = false; -// } -// return b.toString(); -// } -// -// -// public static boolean isAbsoluteFileName(String source) { -// if (isWindows()) -// return (source.length() > 2 && source.charAt(1) == ':') || source.startsWith("\\\\"); -// else -// return source.startsWith("//"); -// } -// -// -// private static boolean isWindows() { -// return System.getProperty("os.name").startsWith("Windows"); -// } -// -// + public static boolean equivalentNumber(String l, String r) { + if (Utilities.noString(l) && Utilities.noString(r)) + return true; + if (Utilities.noString(l) || Utilities.noString(r)) + return false; + l = l.toLowerCase().trim(); + r = r.toLowerCase().trim(); // not that this should make any difference + return l.startsWith(r) || r.startsWith(l); + } + // + // + // public static String getFileExtension(String fn) { + // return fn.contains(".") ? fn.substring(fn.lastIndexOf(".")+1) : ""; + // } + // + // + // public static String unCamelCase(String name) { + // StringBuilder b = new StringBuilder(); + // boolean first = true; + // for (char c : name.toCharArray()) { + // if (Character.isUpperCase(c)) { + // if (!first) + // b.append(" "); + // b.append(Character.toLowerCase(c)); + // } else + // b.append(c); + // first = false; + // } + // return b.toString(); + // } + // + // + // public static boolean isAbsoluteFileName(String source) { + // if (isWindows()) + // return (source.length() > 2 && source.charAt(1) == ':') || source.startsWith("\\\\"); + // else + // return source.startsWith("//"); + // } + // + // + // private static boolean isWindows() { + // return System.getProperty("os.name").startsWith("Windows"); + // } + // + // } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/ucum/Decimal.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/ucum/Decimal.java index 2728f61e9cd..8d7f34bf371 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/ucum/Decimal.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/ucum/Decimal.java @@ -175,10 +175,10 @@ public class Decimal { } private String delete(String value, int offset, int length) { - if (offset == 0) + if (offset == 0){ return value.substring(length); - else - return value.substring(0, offset)+value.substring(offset+length); + } + return value.substring(0, offset)+value.substring(offset+length); } private void setValueScientific(String value) throws UcumException { @@ -214,10 +214,10 @@ public class Decimal { } private String insert(String ins, String value, int offset) { - if (offset == 0) + if (offset == 0) { return ins+value; - else - return value.substring(0, offset)+ins+value.substring(offset); + } + return value.substring(0, offset)+ins+value.substring(offset); } @Override @@ -515,10 +515,10 @@ public class Decimal { int t = c + (dig(s1.charAt(i)) - dig(s2.charAt(i))); if (t < 0) { t = t + 10; - if (i == 0) + if (i == 0) { throw new Error("internal logic error"); - else - s1 = replaceChar(s1, i-1, cdig(dig(s1.charAt(i-1))-1)); + } + s1 = replaceChar(s1, i-1, cdig(dig(s1.charAt(i-1))-1)); } result[i] = cdig(t); } @@ -527,10 +527,10 @@ public class Decimal { } private String replaceChar(String s, int offset, char c) { - if (offset == 0) + if (offset == 0){ return String.valueOf(c)+s.substring(1); - else - return s.substring(0, offset)+c+s.substring(offset+1); + } + return s.substring(0, offset)+c+s.substring(offset+1); } @@ -772,10 +772,10 @@ public class Decimal { int i = 0; while (i < s.length() && s.charAt(i) == '0') i++; - if (i == s.length()) + if (i == s.length()){ return "0"; - else - return s.substring(i); + } + return s.substring(i); } public Decimal divInt(Decimal other) throws UcumException { diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java index ef0d90139d2..88ba6ac1080 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java @@ -180,28 +180,28 @@ public class HierarchicalTableGenerator { pieces.add(piece); return this; } - private List htmlToParagraphPieces(String html) { - List myPieces = new ArrayList(); - String[] paragraphs = html.replace("

", "").split("<\\/p>|
"); - for (int i=0;i htmlFormattingToPieces(String html) { - List myPieces = new ArrayList(); - //Todo: At least handle bold and italics and turn them into formatted spans. (Will need to handle nesting though) - myPieces.add(new Piece(null, html, null)); - - return myPieces; - } +// private List htmlToParagraphPieces(String html) { +// List myPieces = new ArrayList(); +// String[] paragraphs = html.replace("

", "").split("<\\/p>|
"); +// for (int i=0;i htmlFormattingToPieces(String html) { +// List myPieces = new ArrayList(); +// //Todo: At least handle bold and italics and turn them into formatted spans. (Will need to handle nesting though) +// myPieces.add(new Piece(null, html, null)); +// +// return myPieces; +// } public void addStyle(String style) { for (Piece p : pieces) p.addStyle(style); @@ -507,8 +507,8 @@ public class HierarchicalTableGenerator { b.append(new String(Base64.encodeBase64(bytes))); // files.put(filename, b.toString()); return b.toString(); - } else - return corePrefix+filename; + } + return corePrefix+filename; } @@ -558,22 +558,22 @@ public class HierarchicalTableGenerator { b.append(new String(encodeBase64)); files.put(filename, b.toString()); return b.toString(); - } else { - b.append("tbl_bck"); - for (Boolean i : indents) - b.append(i ? "0" : "1"); - if (hasChildren) - b.append("1"); - else - b.append("0"); - b.append(".png"); - String file = Utilities.path(dest, b.toString()); - if (!new File(file).exists()) { - FileOutputStream stream = new FileOutputStream(file); - genImage(indents, hasChildren, stream); - } - return b.toString(); } + b.append("tbl_bck"); + for (Boolean i : indents) + b.append(i ? "0" : "1"); + if (hasChildren) + b.append("1"); + else + b.append("0"); + b.append(".png"); + String file = Utilities.path(dest, b.toString()); + if (!new File(file).exists()) { + //FIXME resource leak + FileOutputStream stream = new FileOutputStream(file); + genImage(indents, hasChildren, stream); + } + return b.toString(); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlDocument.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlDocument.java index 114f187c296..a4c6b19fbdf 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlDocument.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlDocument.java @@ -25,7 +25,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + */ package org.hl7.fhir.utilities.xhtml; /* @@ -51,15 +51,17 @@ package org.hl7.fhir.utilities.xhtml; public class XhtmlDocument extends XhtmlNode { - public XhtmlDocument() { - super(NodeType.Document); - } + private static final long serialVersionUID = -5061185698841745693L; - public XhtmlNode getDocumentElement() { - for (XhtmlNode n : getChildNodes()) { - if (n.getNodeType() == NodeType.Element) - return n; - } - return null; - } + public XhtmlDocument() { + super(NodeType.Document); + } + + public XhtmlNode getDocumentElement() { + for (XhtmlNode n : getChildNodes()) { + if (n.getNodeType() == NodeType.Element) + return n; + } + return null; + } } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index 4dac3bc76d5..a263b3d945c 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -25,7 +25,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + */ package org.hl7.fhir.utilities.xhtml; /* @@ -62,268 +62,268 @@ import ca.uhn.fhir.model.primitive.XhtmlDt; @ca.uhn.fhir.model.api.annotation.DatatypeDef(name="xhtml") public class XhtmlNode implements IBaseXhtml { - private static final long serialVersionUID = -4362547161441436492L; + private static final long serialVersionUID = -4362547161441436492L; - public static class Location { - private int line; - private int column; - public Location(int line, int column) { - super(); - this.line = line; - this.column = column; - } - public int getLine() { - return line; - } - public int getColumn() { - return column; - } - @Override - public String toString() { - return "Line "+Integer.toString(line)+", column "+Integer.toString(column); - } - } + public static class Location { + private int line; + private int column; + public Location(int line, int column) { + super(); + this.line = line; + this.column = column; + } + public int getLine() { + return line; + } + public int getColumn() { + return column; + } + @Override + public String toString() { + return "Line "+Integer.toString(line)+", column "+Integer.toString(column); + } + } - public static final String NBSP = Character.toString((char)0xa0); + public static final String NBSP = Character.toString((char)0xa0); private static final String DECL_XMLNS = " xmlns=\"http://www.w3.org/1999/xhtml\""; - - private Location location; - private NodeType nodeType; - private String name; - private Map attributes = new HashMap(); - private List childNodes = new ArrayList(); - private String content; - public XhtmlNode() { - super(); - } + private Location location; + private NodeType nodeType; + private String name; + private Map attributes = new HashMap(); + private List childNodes = new ArrayList(); + private String content; - - public XhtmlNode(NodeType nodeType, String name) { - super(); - this.nodeType = nodeType; - this.name = name; - } + public XhtmlNode() { + super(); + } - public XhtmlNode(NodeType nodeType) { - super(); - this.nodeType = nodeType; - } - public NodeType getNodeType() { - return nodeType; - } + public XhtmlNode(NodeType nodeType, String name) { + super(); + this.nodeType = nodeType; + this.name = name; + } - public void setNodeType(NodeType nodeType) { - this.nodeType = nodeType; - } + public XhtmlNode(NodeType nodeType) { + super(); + this.nodeType = nodeType; + } - public String getName() { - return name; - } + public NodeType getNodeType() { + return nodeType; + } - public void setName(String name) { - assert name.contains(":") == false : "Name should not contain any : but was " + name; - this.name = name; - } + public void setNodeType(NodeType nodeType) { + this.nodeType = nodeType; + } - public Map getAttributes() { - return attributes; - } + public String getName() { + return name; + } - public List getChildNodes() { - return childNodes; - } + public void setName(String name) { + assert name.contains(":") == false : "Name should not contain any : but was " + name; + this.name = name; + } - public String getContent() { - return content; - } + public Map getAttributes() { + return attributes; + } - public XhtmlNode setContent(String content) { - if (!(nodeType != NodeType.Text || nodeType != NodeType.Comment)) - throw new Error("Wrong node type"); - this.content = content; - return this; - } + public List getChildNodes() { + return childNodes; + } - public XhtmlNode addTag(String name) - { + public String getContent() { + return content; + } - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type. is "+nodeType.toString()); - XhtmlNode node = new XhtmlNode(NodeType.Element); - node.setName(name); - childNodes.add(node); - return node; - } + public XhtmlNode setContent(String content) { + if (!(nodeType != NodeType.Text || nodeType != NodeType.Comment)) + throw new Error("Wrong node type"); + this.content = content; + return this; + } - public XhtmlNode addTag(int index, String name) - { + public XhtmlNode addTag(String name) + { - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type. is "+nodeType.toString()); - XhtmlNode node = new XhtmlNode(NodeType.Element); - node.setName(name); - childNodes.add(index, node); - return node; - } + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type. is "+nodeType.toString()); + XhtmlNode node = new XhtmlNode(NodeType.Element); + node.setName(name); + childNodes.add(node); + return node; + } - public XhtmlNode addComment(String content) - { - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - XhtmlNode node = new XhtmlNode(NodeType.Comment); - node.setContent(content); - childNodes.add(node); - return node; - } + public XhtmlNode addTag(int index, String name) + { - public XhtmlNode addDocType(String content) - { - if (!(nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - XhtmlNode node = new XhtmlNode(NodeType.DocType); - node.setContent(content); - childNodes.add(node); - return node; - } + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type. is "+nodeType.toString()); + XhtmlNode node = new XhtmlNode(NodeType.Element); + node.setName(name); + childNodes.add(index, node); + return node; + } - public XhtmlNode addInstruction(String content) - { - if (!(nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - XhtmlNode node = new XhtmlNode(NodeType.Instruction); - node.setContent(content); - childNodes.add(node); - return node; - } + public XhtmlNode addComment(String content) + { + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + XhtmlNode node = new XhtmlNode(NodeType.Comment); + node.setContent(content); + childNodes.add(node); + return node; + } + + public XhtmlNode addDocType(String content) + { + if (!(nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + XhtmlNode node = new XhtmlNode(NodeType.DocType); + node.setContent(content); + childNodes.add(node); + return node; + } + + public XhtmlNode addInstruction(String content) + { + if (!(nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + XhtmlNode node = new XhtmlNode(NodeType.Instruction); + node.setContent(content); + childNodes.add(node); + return node; + } - public XhtmlNode addText(String content) - { - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - if (content != null) { - XhtmlNode node = new XhtmlNode(NodeType.Text); - node.setContent(content); - childNodes.add(node); - return node; - } else - return null; - } + public XhtmlNode addText(String content) + { + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + if (content != null) { + XhtmlNode node = new XhtmlNode(NodeType.Text); + node.setContent(content); + childNodes.add(node); + return node; + } + return null; + } - public XhtmlNode addText(int index, String content) - { - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - if (content == null) - throw new Error("Content cannot be null"); - - XhtmlNode node = new XhtmlNode(NodeType.Text); - node.setContent(content); - childNodes.add(index, node); - return node; - } + public XhtmlNode addText(int index, String content) + { + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + if (content == null) + throw new Error("Content cannot be null"); - public boolean allChildrenAreText() - { - boolean res = true; - for (XhtmlNode n : childNodes) - res = res && n.getNodeType() == NodeType.Text; - return res; - } + XhtmlNode node = new XhtmlNode(NodeType.Text); + node.setContent(content); + childNodes.add(index, node); + return node; + } - public XhtmlNode getElement(String name) - { - for (XhtmlNode n : childNodes) - if (n.getNodeType() == NodeType.Element && name.equals(n.getName())) - return n; - return null; - } + public boolean allChildrenAreText() + { + boolean res = true; + for (XhtmlNode n : childNodes) + res = res && n.getNodeType() == NodeType.Text; + return res; + } - public XhtmlNode getFirstElement() - { - for (XhtmlNode n : childNodes) - if (n.getNodeType() == NodeType.Element) - return n; - return null; - } + public XhtmlNode getElement(String name) + { + for (XhtmlNode n : childNodes) + if (n.getNodeType() == NodeType.Element && name.equals(n.getName())) + return n; + return null; + } - public String allText() { - StringBuilder b = new StringBuilder(); - for (XhtmlNode n : childNodes) - if (n.getNodeType() == NodeType.Text) - b.append(n.getContent()); - else if (n.getNodeType() == NodeType.Element) - b.append(n.allText()); - return b.toString(); - } + public XhtmlNode getFirstElement() + { + for (XhtmlNode n : childNodes) + if (n.getNodeType() == NodeType.Element) + return n; + return null; + } - public XhtmlNode attribute(String name, String value) { - if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) - throw new Error("Wrong node type"); - if (name == null) - throw new Error("name is null"); - if (value == null) - throw new Error("value is null"); - attributes.put(name, value); - return this; - } + public String allText() { + StringBuilder b = new StringBuilder(); + for (XhtmlNode n : childNodes) + if (n.getNodeType() == NodeType.Text) + b.append(n.getContent()); + else if (n.getNodeType() == NodeType.Element) + b.append(n.allText()); + return b.toString(); + } - public boolean hasAttribute(String name) { - return getAttributes().containsKey(name); - } + public XhtmlNode attribute(String name, String value) { + if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) + throw new Error("Wrong node type"); + if (name == null) + throw new Error("name is null"); + if (value == null) + throw new Error("value is null"); + attributes.put(name, value); + return this; + } - public String getAttribute(String name) { - return getAttributes().get(name); - } + public boolean hasAttribute(String name) { + return getAttributes().containsKey(name); + } - public XhtmlNode setAttribute(String name, String value) { - getAttributes().put(name, value); - return this; - } - - public XhtmlNode copy() { - XhtmlNode dst = new XhtmlNode(nodeType); - dst.name = name; - for (String n : attributes.keySet()) { - dst.attributes.put(n, attributes.get(n)); - } - for (XhtmlNode n : childNodes) - dst.childNodes.add(n.copy()); - dst.content = content; - return dst; - } + public String getAttribute(String name) { + return getAttributes().get(name); + } + + public XhtmlNode setAttribute(String name, String value) { + getAttributes().put(name, value); + return this; + } + + public XhtmlNode copy() { + XhtmlNode dst = new XhtmlNode(nodeType); + dst.name = name; + for (String n : attributes.keySet()) { + dst.attributes.put(n, attributes.get(n)); + } + for (XhtmlNode n : childNodes) + dst.childNodes.add(n.copy()); + dst.content = content; + return dst; + } @Override public boolean isEmpty() { - return (childNodes == null || childNodes.isEmpty()) && content == null; - } + return (childNodes == null || childNodes.isEmpty()) && content == null; + } public boolean equalsDeep(XhtmlNode other) { - if (other == null) { - return false; - } + if (other == null) { + return false; + } - if (!(nodeType == other.nodeType) || !compare(name, other.name) || !compare(content, other.content)) - return false; - if (attributes.size() != other.attributes.size()) - return false; - for (String an : attributes.keySet()) - if (!attributes.get(an).equals(other.attributes.get(an))) - return false; - if (childNodes.size() != other.childNodes.size()) - return false; + if (!(nodeType == other.nodeType) || !compare(name, other.name) || !compare(content, other.content)) + return false; + if (attributes.size() != other.attributes.size()) + return false; + for (String an : attributes.keySet()) + if (!attributes.get(an).equals(other.attributes.get(an))) + return false; + if (childNodes.size() != other.childNodes.size()) + return false; for (int i = 0; i < childNodes.size(); i++) { if (!compareDeep(childNodes.get(i), other.childNodes.get(i))) return false; } return true; - } + } private boolean compare(String s1, String s2) { if (s1 == null && s2 == null) @@ -331,7 +331,7 @@ public class XhtmlNode implements IBaseXhtml { if (s1 == null || s2 == null) return false; return s1.equals(s2); - } + } private static boolean compareDeep(XhtmlNode e1, XhtmlNode e2) { if (e1 == null && e2 == null) @@ -339,18 +339,18 @@ public class XhtmlNode implements IBaseXhtml { if (e1 == null || e2 == null) return false; return e1.equalsDeep(e2); - } - - public String getNsDecl() { - for (String an : attributes.keySet()) { - if (an.equals("xmlns")) { - return attributes.get(an); - } - } - return null; - } - - + } + + public String getNsDecl() { + for (String an : attributes.keySet()) { + if (an.equals("xmlns")) { + return attributes.get(an); + } + } + return null; + } + + @Override public String getValueAsString() { if (isEmpty()) { @@ -376,9 +376,9 @@ public class XhtmlNode implements IBaseXhtml { if (isBlank(theValue)) { return; } - + String val = theValue.trim(); - + if (!val.startsWith("<")) { val = "" + val + ""; } @@ -387,7 +387,7 @@ public class XhtmlNode implements IBaseXhtml { } val = XhtmlDt.preprocessXhtmlNamespaceDeclaration(val); - + try { // TODO: this is ugly XhtmlNode fragment = new XhtmlParser().parseFragment(val); @@ -400,61 +400,64 @@ public class XhtmlNode implements IBaseXhtml { // TODO: composer shouldn't throw exception like this throw new RuntimeException(e); } - + } - public XhtmlNode getElementByIndex(int i) { - int c = 0; - for (XhtmlNode n : childNodes) - if (n.getNodeType() == NodeType.Element) { - if (c == i) - return n; - else - c++; - } - return null; - } + public XhtmlNode getElementByIndex(int i) { + int c = 0; + for (XhtmlNode n : childNodes) + if (n.getNodeType() == NodeType.Element) { + if (c == i){ + return n; + } + c++; + } + return null; + } -@Override -public String getValue() { - return getValueAsString(); -} + @Override + public String getValue() { + return getValueAsString(); + } -@Override -public XhtmlNode setValue(String theValue) throws IllegalArgumentException { - setValueAsString(theValue); - return this; -} + @Override + public XhtmlNode setValue(String theValue) throws IllegalArgumentException { + setValueAsString(theValue); + return this; + } -/** - * Returns false - */ -public boolean hasFormatComment() { - return false; -} + /** + * Returns false + */ + @Override + public boolean hasFormatComment() { + return false; + } -/** - * NOT SUPPORTED - Throws {@link UnsupportedOperationException} - */ -public List getFormatCommentsPre() { - throw new UnsupportedOperationException(); -} + /** + * NOT SUPPORTED - Throws {@link UnsupportedOperationException} + */ + @Override + public List getFormatCommentsPre() { + throw new UnsupportedOperationException(); + } -/** - * NOT SUPPORTED - Throws {@link UnsupportedOperationException} - */ -public List getFormatCommentsPost() { - throw new UnsupportedOperationException(); -} + /** + * NOT SUPPORTED - Throws {@link UnsupportedOperationException} + */ + @Override + public List getFormatCommentsPost() { + throw new UnsupportedOperationException(); + } -public Location getLocation() { - return location; -} + public Location getLocation() { + return location; + } -public void setLocation(Location location) { - this.location = location; -} + public void setLocation(Location location) { + this.location = location; + } } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java index 1a54dc7db73..dda22dde031 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java @@ -227,17 +227,17 @@ public class XhtmlParser { if (validatorMode) return true; boolean ok = attributes.contains(attr) || attributes.contains(elem + "." + attr); - if (ok) + if (ok) { return true; - else - switch (policy) { - case Accept: - return true; - case Drop: - return false; - case Reject: - throw new FHIRFormatError("Illegal HTML attribute " + elem + "." + attr); - } + } + switch (policy) { + case Accept: + return true; + case Drop: + return false; + case Reject: + throw new FHIRFormatError("Illegal HTML attribute " + elem + "." + attr); + } if ((elem + "." + attr).equals("img.src") && !(value.startsWith("#") || value.startsWith("http:") || value.startsWith("https:"))) { switch (policy) { @@ -306,17 +306,17 @@ public class XhtmlParser { if (validatorMode) return true; boolean ok = elements.contains(name); - if (ok) + if (ok){ return true; - else - switch (policy) { - case Accept: - return true; - case Drop: - return false; - case Reject: - throw new FHIRFormatError("Illegal HTML element " + name); - } + } + switch (policy) { + case Accept: + return true; + case Drop: + return false; + case Reject: + throw new FHIRFormatError("Illegal HTML element " + name); + } return false; } @@ -458,26 +458,25 @@ public class XhtmlParser { else if (peekChar() == '/') { readChar(); QName n = new QName(readToTagEnd()); - if (node.getName().equals(n.getName())) + if (node.getName().equals(n.getName())){ return; - else { - if (mustBeWellFormed) - throw new FHIRFormatError("Malformed XHTML: Found \"\" expecting \"\"" + descLoc()); - for (int i = parents.size() - 1; i >= 0; i--) { - if (parents.get(i).getName().equals(n)) - unwindPoint = parents.get(i); - } - if (unwindPoint != null) { - for (int i = parents.size(); i > 0; i--) { - if (i < parents.size() && parents.get(i) == unwindPoint) - return; - if (i == parents.size()) { - parents.get(i - 1).getChildNodes().addAll(node.getChildNodes()); - node.getChildNodes().clear(); - } else { - parents.get(i - 1).getChildNodes().addAll(parents.get(i).getChildNodes()); - parents.get(i).getChildNodes().clear(); - } + } + if (mustBeWellFormed) + throw new FHIRFormatError("Malformed XHTML: Found \"\" expecting \"\"" + descLoc()); + for (int i = parents.size() - 1; i >= 0; i--) { + if (parents.get(i).getName().equals(n)) + unwindPoint = parents.get(i); + } + if (unwindPoint != null) { + for (int i = parents.size(); i > 0; i--) { + if (i < parents.size() && parents.get(i) == unwindPoint) + return; + if (i == parents.size()) { + parents.get(i - 1).getChildNodes().addAll(node.getChildNodes()); + node.getChildNodes().clear(); + } else { + parents.get(i - 1).getChildNodes().addAll(parents.get(i).getChildNodes()); + parents.get(i).getChildNodes().clear(); } } } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java index c943109b184..b36b8d0d704 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLUtil.java @@ -275,38 +275,37 @@ public class XMLUtil { * @return escape string */ public static String escapeXML(String rawContent, String charset, boolean isNoLines) { - if (rawContent == null) + if (rawContent == null){ return ""; - else { - StringBuffer sb = new StringBuffer(); + } + StringBuffer sb = new StringBuffer(); - for (int i = 0; i < rawContent.length(); i++) { - char ch = rawContent.charAt(i); - if (ch == '\'') - sb.append("'"); - else if (ch == '&') - sb.append("&"); - else if (ch == '"') - sb.append("""); - else if (ch == '<') - sb.append("<"); - else if (ch == '>') - sb.append(">"); - else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) - // TODO - why is hashcode the only way to get the unicode number for the character - // in jre 5.0? - sb.append("&#x"+Integer.toHexString(new Character(ch).hashCode()).toUpperCase()+";"); - else if (isNoLines) { - if (ch == '\r') - sb.append(" "); - else if (ch != '\n') - sb.append(ch); - } - else + for (int i = 0; i < rawContent.length(); i++) { + char ch = rawContent.charAt(i); + if (ch == '\'') + sb.append("'"); + else if (ch == '&') + sb.append("&"); + else if (ch == '"') + sb.append("""); + else if (ch == '<') + sb.append("<"); + else if (ch == '>') + sb.append(">"); + else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) + // TODO - why is hashcode the only way to get the unicode number for the character + // in jre 5.0? + sb.append("&#x"+Integer.toHexString(new Character(ch).hashCode()).toUpperCase()+";"); + else if (isNoLines) { + if (ch == '\r') + sb.append(" "); + else if (ch != '\n') sb.append(ch); } - return sb.toString(); + else + sb.append(ch); } + return sb.toString(); } public static Element getFirstChild(Element e) { @@ -435,6 +434,7 @@ public class XMLUtil { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(false); DocumentBuilder builder = factory.newDocumentBuilder(); + //FIXME resource leak return builder.parse(new FileInputStream(filename)); } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java index f66a0915b2c..ab739b9390d 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xml/XMLWriter.java @@ -401,10 +401,10 @@ public class XMLWriter extends OutputStreamWriter implements IXMLWriter { @Override public String getDefaultNamespace() { XMLNamespace ns = findDefaultNamespace(); - if (ns == null) + if (ns == null) { return null; - else - return ns.getNamespace(); + } + return ns.getNamespace(); } /* (non-Javadoc) @@ -519,10 +519,10 @@ public class XMLWriter extends OutputStreamWriter implements IXMLWriter { writePendingComment(); pendingClose = false; } - - if (name == null) { - throw new IOException("name is null"); - } +//death code +// if (name == null) { +// throw new IOException("name is null"); +// } newLevelIfRequired(); levels.current().setName(name); levels.current().setNamespace(namespace); @@ -615,23 +615,22 @@ public class XMLWriter extends OutputStreamWriter implements IXMLWriter { checkStarted(); if (levels.empty()) { throw new IOException("Called exit one too many times"); + } + if (pendingClose) { + write("/>"); + writePendingComment(); + pendingClose = false; } else { - if (pendingClose) { - write("/>"); - writePendingComment(); - pendingClose = false; - } else { - if (levels.current().hasChildren()) - writePretty(); - write("'); - } - levels.pop(); + if (levels.current().hasChildren()) + writePretty(); + write("'); } + levels.pop(); } /* (non-Javadoc) @@ -830,8 +829,8 @@ public class XMLWriter extends OutputStreamWriter implements IXMLWriter { for (int i = 0; i < levels.size() - 1; i++) write(" "); return (levels.size() - 1) * 2; - } else - return 0; + } + return 0; } public int getLineType() { diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 636b56fc038..c8aa3a30c30 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -67,6 +67,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transac ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided. ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1} +ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\") ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/json/JsonLikeStructureTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/json/JsonLikeStructureTest.java index 925340ac1b8..bc120a0e2c6 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/json/JsonLikeStructureTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/json/JsonLikeStructureTest.java @@ -7,11 +7,9 @@ import java.io.StringReader; import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; - public class JsonLikeStructureTest { - private static FhirContext ourCtx; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonLikeStructureTest.class); +// private static FhirContext ourCtx; +// private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonLikeStructureTest.class); private static final String TEST_STRUCTURELOADING_DATA = "{" + diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java new file mode 100644 index 00000000000..f19fa54bf58 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java @@ -0,0 +1,19 @@ +package ca.uhn.fhir.rest.param; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class StringParamTest { + + @Test + public void testEquals() { + StringParam input = new StringParam("foo", true); + + assertTrue(input.equals(input)); + assertFalse(input.equals(null)); + assertFalse(input.equals("")); + assertFalse(input.equals(new StringParam("foo", false))); + } + +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java index f3b689e29ea..d55b7a9a1cc 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/UrlUtilTest.java @@ -1,11 +1,11 @@ package ca.uhn.fhir.util; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import org.junit.Test; -import ca.uhn.fhir.util.UrlUtil.UrlParts; - public class UrlUtilTest { @Test diff --git a/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 9965a51e88b..22e67de435f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -11,7 +11,7 @@ import static org.apache.commons.lang3.StringUtils.defaultIfBlank; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -335,7 +335,7 @@ public abstract class BaseHapiFhirDao implements IDao { continue; } } - + Class type = resourceDefinition.getImplementingClass(); String id = nextId.getIdPart(); if (StringUtils.isBlank(id)) { @@ -677,18 +677,19 @@ public abstract class BaseHapiFhirDao implements IDao { theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE); } } - + protected void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) { if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) { if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) { - throw new InternalErrorException("Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType()); + throw new InternalErrorException( + "Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType()); } } if (theRequestDetails.getUserData().get(PROCESSING_SUB_REQUEST) == Boolean.TRUE) { theRequestDetails.notifyIncomingRequestPreHandled(theOperationType); } - + List interceptors = getConfig().getInterceptors(); if (interceptors == null) { return; @@ -779,7 +780,7 @@ public abstract class BaseHapiFhirDao implements IDao { theEntity.setHasTags(true); } } - + ArrayList existingTags = new ArrayList(); if (theEntity.isHasTags()) { existingTags.addAll(theEntity.getTags()); @@ -937,9 +938,9 @@ public abstract class BaseHapiFhirDao implements IDao { * Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time. * * @param theEntity - * The entity being updated (Do not modify the entity! Undefined behaviour will occur!) + * The entity being updated (Do not modify the entity! Undefined behaviour will occur!) * @param theTag - * The tag + * The tag * @return Returns true if the tag should be removed */ @SuppressWarnings("unused") @@ -951,9 +952,9 @@ public abstract class BaseHapiFhirDao implements IDao { * Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database * * @param theEntity - * The resource + * The resource * @param theResource - * The resource being persisted + * The resource being persisted */ protected void postUpdate(ResourceTable theEntity, T theResource) { // nothing @@ -1009,9 +1010,9 @@ public abstract class BaseHapiFhirDao implements IDao { *

* * @param theEntity - * The entity being updated (Do not modify the entity! Undefined behaviour will occur!) + * The entity being updated (Do not modify the entity! Undefined behaviour will occur!) * @param theTag - * The tag + * The tag * @return Retturns true if the tag should be removed */ protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) { @@ -1072,7 +1073,7 @@ public abstract class BaseHapiFhirDao implements IDao { } } } - + IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion())); R retVal; try { @@ -1241,7 +1242,8 @@ public abstract class BaseHapiFhirDao implements IDao { } /* - * Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the matching + * Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the + * matching * resource. */ if (myConfig.isAllowInlineMatchUrlReferences()) { @@ -1437,7 +1439,7 @@ public abstract class BaseHapiFhirDao implements IDao { theEntity.setResourceLinks(links); theEntity.toString(); - + } // if thePerformIndexing theEntity = myEntityManager.merge(theEntity); @@ -1538,9 +1540,9 @@ public abstract class BaseHapiFhirDao implements IDao { * "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check. * * @param theResource - * The resource that is about to be persisted + * The resource that is about to be persisted * @param theEntityToSave - * TODO + * TODO */ protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) { Object tag = null; @@ -1747,7 +1749,7 @@ public abstract class BaseHapiFhirDao implements IDao { paramMap.add(nextParamName, param); continue; } - + if (Constants.PARAM_COUNT.equals(nextParamName)) { if (paramList.size() > 0 && paramList.get(0).size() > 0) { String intString = paramList.get(0).get(0); @@ -1772,7 +1774,8 @@ public abstract class BaseHapiFhirDao implements IDao { } else { RuntimeSearchParam paramDef = theCallingDao.getSearchParamByName(resourceDef, nextParamName); if (paramDef == null) { - throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName); + throw new InvalidRequestException( + "Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName); } IQueryParameterAnd param = MethodUtil.parseQueryParams(theContext, paramDef, nextParamName, paramList); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 446534ad694..880cd96e91e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -192,7 +192,7 @@ public abstract class BaseHapiFhirResourceDao extends B validateOkToDelete(deleteConflicts, entity); - preDelete(resourceToDelete); + preDelete(resourceToDelete, entity); // Notify interceptors if (theRequestDetails != null) { @@ -722,7 +722,8 @@ public abstract class BaseHapiFhirResourceDao extends B * Subclasses may override to provide behaviour. Invoked within a delete * transaction with the resource that is about to be deleted. */ - protected void preDelete(T theResourceToDelete) { + @SuppressWarnings("unused") + protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete) { // nothing by default } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseSearchParamRegistry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseSearchParamRegistry.java index 8b2cb87df03..8608d26e428 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseSearchParamRegistry.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseSearchParamRegistry.java @@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,6 @@ import java.util.Map; import javax.annotation.PostConstruct; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.springframework.beans.factory.annotation.Autowired; @@ -37,8 +36,6 @@ import ca.uhn.fhir.context.RuntimeSearchParam; public abstract class BaseSearchParamRegistry implements ISearchParamRegistry { - private static final Map EMPTY_SP_MAP = Collections.emptyMap(); - private Map> myBuiltInSearchParams; @Autowired @@ -56,27 +53,32 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry { // nothing by default } - public Map> getBuiltInSearchParams() { + @Override + public Map> getActiveSearchParams() { return myBuiltInSearchParams; } @Override - public Map getActiveSearchParams(String theResourceName) { + public Map getActiveSearchParams(String theResourceName) { Validate.notBlank(theResourceName, "theResourceName must not be blank or null"); return myBuiltInSearchParams.get(theResourceName); } + public Map> getBuiltInSearchParams() { + return myBuiltInSearchParams; + } + @PostConstruct public void postConstruct() { - Map> resourceNameToSearchParams = new HashMap>(); + Map> resourceNameToSearchParams = new HashMap>(); for (IFhirResourceDao nextDao : myDaos) { RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType()); String nextResourceName = nextResDef.getName(); HashMap nameToParam = new HashMap(); resourceNameToSearchParams.put(nextResourceName, nameToParam); - + for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) { nameToParam.put(nextSp.getName(), nextSp); } @@ -85,13 +87,4 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry { myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams); } - @Override - public Collection getAllSearchParams(String theResourceName) { - Validate.notBlank(theResourceName, "theResourceName must not be null or blank"); - - Map map = myBuiltInSearchParams.get(theResourceName); - map = ObjectUtils.defaultIfNull(map, EMPTY_SP_MAP); - return Collections.unmodifiableCollection(map.values()); - } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java index 34b82976b90..62ae1312e66 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java @@ -56,6 +56,7 @@ import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.FhirTerser; @@ -133,8 +134,11 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou ValidationResult result; if (isNotBlank(theRawResource)) { result = validator.validateWithResult(theRawResource); - } else { + } else if (theResource != null) { result = validator.validateWithResult(theResource); + } else { + String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource"); + throw new InvalidRequestException(msg); } if (result.isSuccessful()) { @@ -160,11 +164,11 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou boolean hasId = theCtx.getResource().getIdElement().hasIdPart(); if (myMode == ValidationModeEnum.CREATE) { if (hasId) { - throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create"); + throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create"); } } else if (myMode == ValidationModeEnum.UPDATE) { if (hasId == false) { - throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update"); + throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update"); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index abbbd8b2214..2af6fbc3f04 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -492,7 +492,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); - updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, shouldUpdate, updateTime); + if (shouldUpdate) { + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, true, updateTime); + } } myEntityManager.flush(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchParamRegistry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchParamRegistry.java index eea349770cd..58a7d489fd0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchParamRegistry.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchParamRegistry.java @@ -1,36 +1,15 @@ package ca.uhn.fhir.jpa.dao; -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2017 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.util.Collection; import java.util.Map; import ca.uhn.fhir.context.RuntimeSearchParam; public interface ISearchParamRegistry { - Map getActiveSearchParams(String theResourceName); - - Collection getAllSearchParams(String theResourceName); - void forceRefresh(); + Map> getActiveSearchParams(); + + Map getActiveSearchParams(String theResourceName); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 3106f9bc0b8..7cff1643bb2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -2309,6 +2309,11 @@ public class SearchBuilder { public int size() { return myPids.size(); } + + @Override + public String getUuid() { + return null; + } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamCoordsDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamCoordsDao.java new file mode 100644 index 00000000000..2bf1e05ab6e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamCoordsDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords; + +public interface IResourceIndexedSearchParamCoordsDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamDateDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamDateDao.java new file mode 100644 index 00000000000..d4f05ff8c75 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamDateDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; + +public interface IResourceIndexedSearchParamDateDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamNumberDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamNumberDao.java new file mode 100644 index 00000000000..9385dd17529 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamNumberDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; + +public interface IResourceIndexedSearchParamNumberDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamQuantityDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamQuantityDao.java new file mode 100644 index 00000000000..e6948e48a28 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamQuantityDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity; + +public interface IResourceIndexedSearchParamQuantityDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamStringDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamStringDao.java new file mode 100644 index 00000000000..7c9b13b500e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamStringDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; + +public interface IResourceIndexedSearchParamStringDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamTokenDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamTokenDao.java new file mode 100644 index 00000000000..03813879786 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedSearchParamTokenDao.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.springframework.data.jpa.repository.JpaRepository; + +import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; + +public interface IResourceIndexedSearchParamTokenDao extends JpaRepository { + // nothing yet +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3.java index 571c5e06c99..9da4a0c7c92 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3.java @@ -54,6 +54,7 @@ import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.FhirTerser; @@ -143,7 +144,8 @@ public class FhirResourceDaoDstu3 extends BaseHapiFhirRe if (resourceToValidateById != null) { result = validator.validateWithResult(resourceToValidateById); } else { - throw new InvalidRequestException("No resource supplied for $validate operation (resource is required unless mode is \"delete\")"); + String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource"); + throw new InvalidRequestException(msg); } } else if (isNotBlank(theRawResource)) { result = validator.validateWithResult(theRawResource); @@ -180,11 +182,11 @@ public class FhirResourceDaoDstu3 extends BaseHapiFhirRe boolean hasId = theCtx.getResource().getIdElement().hasIdPart(); if (myMode == ValidationModeEnum.CREATE) { if (hasId) { - throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create"); + throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create"); } } else if (myMode == ValidationModeEnum.UPDATE) { if (hasId == false) { - throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update"); + throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update"); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java index f816fe2f6d4..5432daf5206 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java @@ -2,8 +2,6 @@ package ca.uhn.fhir.jpa.dao.dstu3; import static org.apache.commons.lang3.StringUtils.isBlank; -import java.util.List; - /* * #%L * HAPI FHIR JPA Server @@ -34,20 +32,23 @@ import org.springframework.scheduling.annotation.Scheduled; import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.entity.ResourceTable; -import ca.uhn.fhir.jpa.util.DeleteConflict; import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.util.ElementUtil; public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 implements IFhirResourceDaoSearchParameter { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class); + @Autowired + private ISearchParamRegistry mySearchParamRegistry; + @Autowired private IFhirSystemDao mySystemDao; - private void markAffectedResources(SearchParameter theResource) { + protected void markAffectedResources(SearchParameter theResource) { if (theResource != null) { String expression = theResource.getExpression(); String resourceType = expression.substring(0, expression.indexOf('.')); @@ -55,8 +56,9 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 return retVal; - // ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion(); + // ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion(); // - // ValueSet retVal = new ValueSet(); - // retVal.getMeta().setLastUpdated(new Date()); - // retVal.setExpansion(expansion); - // return retVal; + // ValueSet retVal = new ValueSet(); + // retVal.getMeta().setLastUpdated(new Date()); + // retVal.setExpansion(expansion); + // return retVal; } private void validateIncludes(String name, List listToValidate) { @@ -137,11 +138,11 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 public ValueSet expand(ValueSet source, String theFilter) { ValueSet toExpand = new ValueSet(); -// for (UriType next : source.getCompose().getInclude()) { -// ConceptSetComponent include = toExpand.getCompose().addInclude(); -// include.setSystem(next.getValue()); -// addFilterIfPresent(theFilter, include); -// } + // for (UriType next : source.getCompose().getInclude()) { + // ConceptSetComponent include = toExpand.getCompose().addInclude(); + // include.setSystem(next.getValue()); + // addFilterIfPresent(theFilter, include); + // } for (ConceptSetComponent next : source.getCompose().getInclude()) { toExpand.getCompose().addInclude(next); @@ -155,18 +156,41 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude()); ValueSet retVal = doExpand(toExpand); + + if (isNotBlank(theFilter)) { + applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter); + } + return retVal; } + private void applyFilter(IntegerType theTotalElement, List theContains, String theFilter) { + + for (int idx = 0; idx < theContains.size(); idx++) { + ValueSetExpansionContainsComponent next = theContains.get(idx); + if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) { + theContains.remove(idx); + idx--; + if (theTotalElement.getValue() != null) { + theTotalElement.setValue(theTotalElement.getValue() - 1); + } + } + applyFilter(theTotalElement, next.getContains(), theFilter); + } + } + private void addFilterIfPresent(String theFilter, ConceptSetComponent include) { - if (isNotBlank(theFilter)) { - include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter); + if (ElementUtil.isEmpty(include.getConcept())) { + if (isNotBlank(theFilter)) { + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter); + } } } @Override - public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, Coding theCoding, + public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, + IPrimitiveType theSystem, IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { List valueSetIds = Collections.emptyList(); @@ -226,7 +250,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 return thePrimitive != null ? thePrimitive.getValue() : null; } - private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List contains, String theSystem, String theCode, Coding theCoding, CodeableConcept theCodeableConcept) { + private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List contains, String theSystem, String theCode, + Coding theCoding, CodeableConcept theCodeableConcept) { for (ValueSetExpansionContainsComponent nextCode : contains) { ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept); if (result != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 6a38958f9d1..51b3caa17f1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -383,7 +383,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); res.setId((String) null); DaoMethodOutcome outcome; - outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails); + String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); + outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res); entriesToProcess.put(nextRespEntry, outcome.getEntity()); if (outcome.getCreated() == false) { @@ -489,7 +490,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); - updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, shouldUpdate, updateTime); + if (shouldUpdate) { + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, true, updateTime); + } } myEntityManager.flush(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java index ed6612fc852..d6f631cbebd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3.java @@ -50,6 +50,7 @@ import org.hl7.fhir.dstu3.model.ContactPoint; import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.Duration; import org.hl7.fhir.dstu3.model.Enumeration; +import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Identifier; import org.hl7.fhir.dstu3.model.IntegerType; @@ -520,7 +521,16 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen // } for (Object nextObject : extractValues(nextPath, theResource)) { + + if (nextObject instanceof Extension) { + Extension nextExtension = (Extension)nextObject; + nextObject = nextExtension.getValue(); + } + if (nextObject == null) { + continue; + } + // Patient:language if (nextObject instanceof PatientCommunicationComponent) { PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamRegistryDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamRegistryDstu3.java index 9faf743d56e..2b3b967e2a6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamRegistryDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamRegistryDstu3.java @@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -35,6 +35,7 @@ import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.SearchParameter; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.beans.factory.annotation.Autowired; import ca.uhn.fhir.context.RuntimeSearchParam; @@ -50,19 +51,43 @@ import ca.uhn.fhir.rest.server.IBundleProvider; public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class); - @Autowired - private IFhirResourceDao mySpDao; - - private long myLastRefresh; - private volatile Map> myActiveSearchParams; @Autowired private DaoConfig myDaoConfig; + private long myLastRefresh; + + @Autowired + private IFhirResourceDao mySpDao; + + @Override + public void forceRefresh() { + myLastRefresh = 0; + } + + @Override + public Map> getActiveSearchParams() { + refreshCacheIfNeccesary(); + return myActiveSearchParams; + } + @Override public Map getActiveSearchParams(String theResourceName) { + refreshCacheIfNeccesary(); + return myActiveSearchParams.get(theResourceName); + } + private Map getSearchParamMap(Map> searchParams, String theResourceName) { + Map retVal = searchParams.get(theResourceName); + if (retVal == null) { + retVal = new HashMap(); + searchParams.put(theResourceName, retVal); + } + return retVal; + } + + private void refreshCacheIfNeccesary() { long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE; if (System.currentTimeMillis() - refreshInterval > myLastRefresh) { StopWatch sw = new StopWatch(); @@ -91,7 +116,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { if (runtimeSp == null) { continue; } - + int dotIdx = runtimeSp.getPath().indexOf('.'); if (dotIdx == -1) { ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath()); @@ -113,14 +138,14 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) { nextSp = null; } - + if (!activeSearchParams.containsKey(nextEntry.getKey())) { activeSearchParams.put(nextEntry.getKey(), new HashMap()); } if (activeSearchParams.containsKey(nextEntry.getKey())) { - ourLog.info("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName); + ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName); } - + if (nextSp != null) { activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp); } else { @@ -134,22 +159,6 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { myLastRefresh = System.currentTimeMillis(); ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis()); } - - return myActiveSearchParams.get(theResourceName); - } - - @Override - public void forceRefresh() { - myLastRefresh = 0; - } - - private Map getSearchParamMap(Map> searchParams, String theResourceName) { - Map retVal = searchParams.get(theResourceName); - if (retVal == null) { - retVal = new HashMap(); - searchParams.put(theResourceName, retVal); - } - return retVal; } private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) { @@ -208,7 +217,9 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { return null; } - RuntimeSearchParam retVal = new RuntimeSearchParam(name, description, path, paramType, providesMembershipInCompartments, targets, status); + IIdType id = theNextSp.getIdElement(); + String uri = ""; + RuntimeSearchParam retVal = new RuntimeSearchParam(id, uri, name, description, path, paramType, null, providesMembershipInCompartments, targets, status); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 3a931961c74..ee4c6286301 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -70,6 +70,10 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { return myResourcePid; } + public String getResourceType() { + return myResourceType; + } + public void setParamName(String theName) { myParamName = theName; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java index 87c51527b57..be65fc85d33 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.search; import javax.persistence.EntityManager; +import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; @@ -30,10 +31,12 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.IDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; +import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IBundleProvider; +import ca.uhn.fhir.rest.server.IPagingProvider; -public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider { +public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider { @Autowired private PlatformTransactionManager thePlatformTransactionManager; @@ -46,29 +49,36 @@ public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider { @Autowired private IFhirSystemDao theDao; + /** + * Constructor + * @deprecated Use {@link DatabaseBackedPagingProvider} as this constructor has no purpose + */ + @Deprecated public DatabaseBackedPagingProvider(int theSize) { - super(theSize); + this(); + } + + /** + * Constructor + */ + public DatabaseBackedPagingProvider() { + super(); } @Override public synchronized IBundleProvider retrieveResultList(String theId) { - IBundleProvider retVal = super.retrieveResultList(theId); - if (retVal == null) { - PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao); - if (!provider.ensureSearchEntityLoaded()) { - return null; - } - retVal = provider; + PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao); + if (!provider.ensureSearchEntityLoaded()) { + return null; } - return retVal; + return provider; } @Override public synchronized String storeResultList(IBundleProvider theList) { - if (theList instanceof PersistedJpaBundleProvider) { - return ((PersistedJpaBundleProvider)theList).getSearchUuid(); - } - return super.storeResultList(theList); + String uuid = theList.getUuid(); + Validate.notNull(uuid); + return uuid; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index f9fd48f57af..4d1c15cae53 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -19,13 +19,7 @@ package ca.uhn.fhir.jpa.search; * limitations under the License. * #L% */ - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import javax.persistence.EntityManager; import javax.persistence.NoResultException; @@ -49,11 +43,7 @@ import ca.uhn.fhir.jpa.dao.IDao; import ca.uhn.fhir.jpa.dao.SearchBuilder; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; -import ca.uhn.fhir.jpa.entity.BaseHasResource; -import ca.uhn.fhir.jpa.entity.ResourceHistoryTable; -import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.entity.SearchResult; -import ca.uhn.fhir.jpa.entity.SearchTypeEnum; +import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.server.IBundleProvider; @@ -220,7 +210,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { }); } - public String getSearchUuid() { + public String getUuid() { return myUuid; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java index d589746e7a8..00b6ebccd30 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { @@ -161,7 +162,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must not be populated")); } @@ -184,7 +185,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must be populated")); } @@ -207,7 +208,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must be populated")); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 113709d8c6a..bd77c162878 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -19,10 +19,12 @@ import static org.mockito.Mockito.verify; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; +import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Test; @@ -73,6 +75,43 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class); + /** + * Per a message on the mailing list + */ + @Test + public void testTransactionWithPostDoesntUpdate() throws Exception { + + // First bundle (name is Joshua) + + String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8); + Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + Bundle response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id = response.getEntry().get(0).getResponse().getLocation(); + + // Now the second (name is Adam, shouldn't get used) + + input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8); + request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id2 = response.getEntry().get(0).getResponse().getLocation(); + assertEquals(id, id2); + + Patient patient = myPatientDao.read(new IdType(id), mySrd); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient)); + assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); + } + + @Test public void testReindexing() { Patient p = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchCustomSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchCustomSearchParamTest.java index 501264d3476..ddfd3744dae 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchCustomSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchCustomSearchParamTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.dao.dstu3; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -9,9 +8,11 @@ import static org.junit.Assert.fail; import java.util.List; +import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.SearchParameter; +import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Test; @@ -19,7 +20,6 @@ import org.junit.Test; import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.IBundleProvider; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; @@ -29,6 +29,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu @Test public void testCreateInvalidParamInvalidResourceName() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -43,9 +44,27 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu } } + @Test + public void testCreateInvalidNoBase() { + SearchParameter fooSp = new SearchParameter(); + fooSp.setCode("foo"); + fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); + fooSp.setTitle("FOO SP"); + fooSp.setExpression("Patient.gender"); + fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); + fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); + try { + mySearchParameterDao.create(fooSp, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("SearchParameter.base is missing", e.getMessage()); + } + } + @Test public void testCreateInvalidParamMismatchedResourceName() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -63,6 +82,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu @Test public void testCreateInvalidParamNoPath() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -79,6 +99,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu @Test public void testCreateInvalidParamNoResourceName() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -97,6 +118,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu public void testCreateInvalidParamParamNullStatus() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -112,10 +134,65 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu } + @Test + public void testExtensionWithNoValueIndexesWithoutFailure() { + SearchParameter eyeColourSp = new SearchParameter(); + eyeColourSp.addBase("Patient"); + eyeColourSp.setCode("eyecolour"); + eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); + eyeColourSp.setTitle("Eye Colour"); + eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')"); + eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); + eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); + mySearchParameterDao.create(eyeColourSp, mySrd); + + mySearchParamRegsitry.forceRefresh(); + + Patient p1 = new Patient(); + p1.setActive(true); + p1.addExtension().setUrl("http://acme.org/eyecolour").addExtension().setUrl("http://foo").setValue(new StringType("VAL")); + IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless(); + + } + + @Test + public void testSearchForExtension() { + SearchParameter eyeColourSp = new SearchParameter(); + eyeColourSp.addBase("Patient"); + eyeColourSp.setCode("eyecolour"); + eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); + eyeColourSp.setTitle("Eye Colour"); + eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')"); + eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); + eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); + mySearchParameterDao.create(eyeColourSp, mySrd); + + mySearchParamRegsitry.forceRefresh(); + + Patient p1 = new Patient(); + p1.setActive(true); + p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue")); + IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless(); + + Patient p2 = new Patient(); + p2.setActive(true); + p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green")); + IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless(); + + // Try with custom gender SP + SearchParameterMap map = new SearchParameterMap(); + map.add("eyecolour", new TokenParam(null, "blue")); + IBundleProvider results = myPatientDao.search(map); + List foundResources = toUnqualifiedVersionlessIdValues(results); + assertThat(foundResources, contains(p1id.getValue())); + + } + @Test public void testSearchWithCustomParam() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -154,7 +231,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu // Delete the param mySearchParameterDao.delete(spId, mySrd); - + mySearchParamRegsitry.forceRefresh(); mySystemDao.performReindexingPass(100); @@ -169,6 +246,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu public void testSearchWithCustomParamDraft() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 414622d3a30..06691b43a41 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -16,6 +16,7 @@ import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceCategory; import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceClinicalStatus; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent; @@ -53,6 +54,9 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + @Autowired + private IHapiTerminologySvc myHapiTerminologySvc; + @After public void after() { myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); @@ -116,6 +120,37 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { createLocalVs(codeSystem); } + private CodeSystem createExternalCsDogs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + cs.setResourceVersionId(table.getVersion()); + + TermConcept hello = new TermConcept(cs, "hello").setDisplay("Hello"); + cs.getConcepts().add(hello); + + TermConcept goodbye = new TermConcept(cs, "goodbye").setDisplay("Goodbye"); + cs.getConcepts().add(goodbye); + + TermConcept dogs = new TermConcept(cs, "dogs").setDisplay("Dogs"); + cs.getConcepts().add(dogs); + + TermConcept labrador = new TermConcept(cs, "labrador").setDisplay("Labrador"); + dogs.addChild(labrador, RelationshipTypeEnum.ISA); + + TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); + dogs.addChild(beagle, RelationshipTypeEnum.ISA); + + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + return codeSystem; + } + private void createLocalCsAndVs() { //@formatter:off CodeSystem codeSystem = new CodeSystem(); @@ -144,6 +179,22 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { myValueSetDao.create(valueSet, mySrd); } + private void logAndValidateValueSet(ValueSet theResult) { + IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true); + String encoded = parser.encodeResourceToString(theResult); + ourLog.info(encoded); + + FhirValidator validator = myFhirCtx.newValidator(); + validator.setValidateAgainstStandardSchema(true); + validator.setValidateAgainstStandardSchematron(true); + ValidationResult result = validator.validateWithResult(theResult); + + if (!result.isSuccessful()) { + ourLog.info(parser.encodeResourceToString(result.toOperationOutcome())); + fail(parser.encodeResourceToString(result.toOperationOutcome())); + } + } + @Test public void testCodeSystemCreateDuplicateFails() { CodeSystem codeSystem = new CodeSystem(); @@ -162,28 +213,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } } - @Test - public void testLookupSnomed() { - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl("http://snomed.info/sct"); - codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - - ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); - - TermCodeSystemVersion cs = new TermCodeSystemVersion(); - cs.setResource(table); - cs.setResourceVersionId(table.getVersion()); - TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); - cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs); - - StringType code = new StringType("ParentA"); - StringType system = new StringType("http://snomed.info/sct"); - LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); - assertEquals(true, outcome.isFound()); - } - @Test public void testCodeSystemWithDefinedCodes() { //@formatter:off @@ -207,170 +236,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } - @Test - public void testExpandWithExcludeInExternalValueSet() { - createExternalCsAndLocalVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - - ConceptSetComponent exclude = vs.getCompose().addExclude(); - exclude.setSystem(URL_MY_CODE_SYSTEM); - exclude.addConcept().setCode("childAA"); - exclude.addConcept().setCode("childAAA"); - - ValueSet result = myValueSetDao.expand(vs, null); - logAndValidateValueSet(result); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA")); - } - - private void logAndValidateValueSet(ValueSet theResult) { - IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true); - String encoded = parser.encodeResourceToString(theResult); - ourLog.info(encoded); - - FhirValidator validator = myFhirCtx.newValidator(); - validator.setValidateAgainstStandardSchema(true); - validator.setValidateAgainstStandardSchematron(true); - ValidationResult result = validator.validateWithResult(theResult); - - if (!result.isSuccessful()) { - ourLog.info(parser.encodeResourceToString(result.toOperationOutcome())); - fail(parser.encodeResourceToString(result.toOperationOutcome())); - } - } - - @Test - public void testExpandWithInvalidExclude() { - createExternalCsAndLocalVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - - /* - * No system set on exclude - */ - ConceptSetComponent exclude = vs.getCompose().addExclude(); - exclude.addConcept().setCode("childAA"); - exclude.addConcept().setCode("childAAA"); - try { - myValueSetDao.expand(vs, null); - fail(); - } catch (InvalidRequestException e) { - assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage()); - } - } - - @Test - public void testExpandWithNoResultsInLocalValueSet1() { - createLocalCsAndVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addConcept().setCode("ZZZZ"); - - try { - myValueSetDao.expand(vs, null); - fail(); - } catch (InvalidRequestException e) { - assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage()); - } - - } - - @Test - public void testReindex() { - createLocalCsAndVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addConcept().setCode("ZZZZ"); - - mySystemDao.markAllResourcesForReindexing(); - mySystemDao.performReindexingPass(null); - myTermSvc.saveDeferred(); - mySystemDao.performReindexingPass(null); - myTermSvc.saveDeferred(); - - // Again - mySystemDao.markAllResourcesForReindexing(); - mySystemDao.performReindexingPass(null); - myTermSvc.saveDeferred(); - mySystemDao.performReindexingPass(null); - myTermSvc.saveDeferred(); - - } - - @Test - public void testExpandWithNoResultsInLocalValueSet2() { - createLocalCsAndVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM + "AA"); - include.addConcept().setCode("A"); - - try { - myValueSetDao.expand(vs, null); - fail(); - } catch (InvalidRequestException e) { - assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage()); - } - } - - @Test - public void testExpandWithIsAInExternalValueSet() { - createExternalCsAndLocalVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept"); - - ValueSet result = myValueSetDao.expand(vs, null); - logAndValidateValueSet(result); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); - - } - - @Autowired - private IHapiTerminologySvc myHapiTerminologySvc; - - @Test - public void testExpandWithIsAInExternalValueSetReindex() { - BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true); - - createExternalCsAndLocalVs(); - - mySystemDao.markAllResourcesForReindexing(); - - mySystemDao.performReindexingPass(100); - mySystemDao.performReindexingPass(100); - myHapiTerminologySvc.saveDeferred(); - myHapiTerminologySvc.saveDeferred(); - myHapiTerminologySvc.saveDeferred(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept"); - - ValueSet result = myValueSetDao.expand(vs, null); - logAndValidateValueSet(result); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); - - } - @Test public void testExpandInvalid() { createExternalCsAndLocalVs(); @@ -390,43 +255,110 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } @Test - public void testExpandWithSystemAndCodesInExternalValueSet() { - createExternalCsAndLocalVs(); + public void testExpandWithCodesAndDisplayFilterBlank() { + CodeSystem codeSystem = createExternalCsDogs(); - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addConcept().setCode("ParentA"); - include.addConcept().setCode("childAA"); - include.addConcept().setCode("childAAA"); + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addConcept(new ConceptReferenceComponent().setCode("hello")) + .addConcept(new ConceptReferenceComponent().setCode("goodbye")); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addFilter() + .setProperty("concept") + .setOp(FilterOperator.ISA) + .setValue("dogs"); + + myValueSetDao.create(valueSet, mySrd); - ValueSet result = myValueSetDao.expand(vs, null); + ValueSet result = myValueSetDao.expand(valueSet, ""); logAndValidateValueSet(result); + assertEquals(4, result.getExpansion().getTotal()); ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA")); + assertThat(codes, containsInAnyOrder("hello", "goodbye", "labrador", "beagle")); - int idx = codes.indexOf("childAA"); - assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode()); - assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay()); - assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); } @Test - public void testExpandWithSystemAndFilterInExternalValueSet() { - createExternalCsAndLocalVs(); + public void testExpandWithCodesAndDisplayFilterPartialOnFilter() { + CodeSystem codeSystem = createExternalCsDogs(); - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addConcept(new ConceptReferenceComponent().setCode("hello")) + .addConcept(new ConceptReferenceComponent().setCode("goodbye")); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addFilter() + .setProperty("concept") + .setOp(FilterOperator.ISA) + .setValue("dogs"); + + myValueSetDao.create(valueSet, mySrd); - include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B"); - - ValueSet result = myValueSetDao.expand(vs, null); + ValueSet result = myValueSetDao.expand(valueSet, "lab"); logAndValidateValueSet(result); + assertEquals(1, result.getExpansion().getTotal()); ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentB")); + assertThat(codes, containsInAnyOrder("labrador")); + + } + + @Test + public void testExpandWithCodesAndDisplayFilterPartialOnCodes() { + CodeSystem codeSystem = createExternalCsDogs(); + + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addConcept(new ConceptReferenceComponent().setCode("hello")) + .addConcept(new ConceptReferenceComponent().setCode("goodbye")); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()) + .addFilter() + .setProperty("concept") + .setOp(FilterOperator.ISA) + .setValue("dogs"); + + myValueSetDao.create(valueSet, mySrd); + + ValueSet result = myValueSetDao.expand(valueSet, "hel"); + logAndValidateValueSet(result); + + assertEquals(1, result.getExpansion().getTotal()); + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("hello")); + + } + + @Test + public void testExpandWithCodesAndDisplayFilterPartialOnExpansion() { + CodeSystem codeSystem = createExternalCsDogs(); + + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl()); + myValueSetDao.create(valueSet, mySrd); + + ValueSet result = myValueSetDao.expand(valueSet, "lab"); + logAndValidateValueSet(result); + + assertEquals(1, result.getExpansion().getTotal()); + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("labrador")); } @@ -463,6 +395,127 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } + @Test + public void testExpandWithExcludeInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + + ConceptSetComponent exclude = vs.getCompose().addExclude(); + exclude.setSystem(URL_MY_CODE_SYSTEM); + exclude.addConcept().setCode("childAA"); + exclude.addConcept().setCode("childAAA"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA")); + } + + @Test + public void testExpandWithInvalidExclude() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + + /* + * No system set on exclude + */ + ConceptSetComponent exclude = vs.getCompose().addExclude(); + exclude.addConcept().setCode("childAA"); + exclude.addConcept().setCode("childAAA"); + try { + myValueSetDao.expand(vs, null); + fail(); + } catch (InvalidRequestException e) { + assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage()); + } + } + + @Test + public void testExpandWithIsAInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); + + } + + @Test + public void testExpandWithIsAInExternalValueSetReindex() { + BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true); + + createExternalCsAndLocalVs(); + + mySystemDao.markAllResourcesForReindexing(); + + mySystemDao.performReindexingPass(100); + mySystemDao.performReindexingPass(100); + myHapiTerminologySvc.saveDeferred(); + myHapiTerminologySvc.saveDeferred(); + myHapiTerminologySvc.saveDeferred(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); + + } + + @Test + public void testExpandWithNoResultsInLocalValueSet1() { + createLocalCsAndVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("ZZZZ"); + + try { + myValueSetDao.expand(vs, null); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage()); + } + + } + + @Test + public void testExpandWithNoResultsInLocalValueSet2() { + createLocalCsAndVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM + "AA"); + include.addConcept().setCode("A"); + + try { + myValueSetDao.expand(vs, null); + fail(); + } catch (InvalidRequestException e) { + assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage()); + } + } + @Test public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() { createLocalCsAndVs(); @@ -493,6 +546,29 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { // } + @Test + public void testExpandWithSystemAndCodesInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("ParentA"); + include.addConcept().setCode("childAA"); + include.addConcept().setCode("childAAA"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA")); + + int idx = codes.indexOf("childAA"); + assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode()); + assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay()); + assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); + } + @Test public void testExpandWithSystemAndCodesInLocalValueSet() { createLocalCsAndVs(); @@ -520,6 +596,43 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { // } + @Test + public void testExpandWithSystemAndDisplayFilterBlank() { + CodeSystem codeSystem = createExternalCsDogs(); + + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose() + .addInclude() + .setSystem(codeSystem.getUrl()); + + ValueSet result = myValueSetDao.expand(valueSet, ""); + logAndValidateValueSet(result); + + assertEquals(5, result.getExpansion().getTotal()); + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("hello", "goodbye", "dogs", "labrador", "beagle")); + + } + + @Test + public void testExpandWithSystemAndFilterInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentB")); + + } + @Test public void testIndexingIsDeferredForLargeCodeSystems() { myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); @@ -560,6 +673,28 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { assertThat(encoded, containsStringIgnoringCase("")); } + @Test + public void testLookupSnomed() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl("http://snomed.info/sct"); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + cs.setResourceVersionId(table.getVersion()); + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs); + + StringType code = new StringType("ParentA"); + StringType system = new StringType("http://snomed.info/sct"); + LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); + assertEquals(true, outcome.isFound()); + } + /** * Can't currently abort costly */ @@ -594,6 +729,30 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } } + @Test + public void testReindex() { + createLocalCsAndVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("ZZZZ"); + + mySystemDao.markAllResourcesForReindexing(); + mySystemDao.performReindexingPass(null); + myTermSvc.saveDeferred(); + mySystemDao.performReindexingPass(null); + myTermSvc.saveDeferred(); + + // Again + mySystemDao.markAllResourcesForReindexing(); + mySystemDao.performReindexingPass(null); + myTermSvc.saveDeferred(); + mySystemDao.performReindexingPass(null); + myTermSvc.saveDeferred(); + + } + @Test public void testSearchCodeAboveLocalCodesystem() { createLocalCsAndVs(); @@ -636,107 +795,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } - @Test - public void testSearchCodeBelowLocalCodesystem() { - createLocalCsAndVs(); - - Observation obsAA = new Observation(); - obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); - IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsBA = new Observation(); - obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); - IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsCA = new Observation(); - obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); - IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue())); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - - } - - @Test - public void testSearchCodeInBuiltInValueSet() { - AllergyIntolerance ai1 = new AllergyIntolerance(); - ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE); - String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue(); - - AllergyIntolerance ai2 = new AllergyIntolerance(); - ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED); - String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue(); - - AllergyIntolerance ai3 = new AllergyIntolerance(); - ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE); - String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue(); - - SearchParameterMap params; - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3)); - - // No codes in this one - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty()); - - // Invalid VS - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN)); - try { - myAllergyIntoleranceDao.search(params); - } catch (InvalidRequestException e) { - assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage()); - } - - } - - - /** - * Todo: not yet implemented - */ - @Test - @Ignore - public void testSearchCodeNotInBuiltInValueSet() { - AllergyIntolerance ai1 = new AllergyIntolerance(); - ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE); - String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue(); - - AllergyIntolerance ai2 = new AllergyIntolerance(); - ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED); - String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue(); - - AllergyIntolerance ai3 = new AllergyIntolerance(); - ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE); - String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue(); - - SearchParameterMap params; - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty()); - - // No codes in this one - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3)); - - // Invalid VS - params = new SearchParameterMap(); - params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN)); - try { - myAllergyIntoleranceDao.search(params); - } catch (InvalidRequestException e) { - assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage()); - } - - } - @Test public void testSearchCodeBelowBuiltInCodesystem() { AllergyIntolerance ai1 = new AllergyIntolerance(); @@ -824,6 +882,68 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } + + @Test + public void testSearchCodeBelowLocalCodesystem() { + createLocalCsAndVs(); + + Observation obsAA = new Observation(); + obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); + IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsBA = new Observation(); + obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); + IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsCA = new Observation(); + obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); + IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue())); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + + } + + @Test + public void testSearchCodeInBuiltInValueSet() { + AllergyIntolerance ai1 = new AllergyIntolerance(); + ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE); + String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue(); + + AllergyIntolerance ai2 = new AllergyIntolerance(); + ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED); + String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue(); + + AllergyIntolerance ai3 = new AllergyIntolerance(); + ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE); + String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap params; + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3)); + + // No codes in this one + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty()); + + // Invalid VS + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN)); + try { + myAllergyIntoleranceDao.search(params); + } catch (InvalidRequestException e) { + assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage()); + } + + } + @Test public void testSearchCodeInEmptyValueSet() { ValueSet valueSet = new ValueSet(); @@ -848,40 +968,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { ourLog.info("testSearchCodeInEmptyValueSet done"); } - @Test - public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() { - ValueSet valueSet = new ValueSet(); - valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS"); - valueSet.setUrl(URL_MY_VALUE_SET); - IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params; - - ourLog.info("testSearchCodeInEmptyValueSet without status"); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - try { - myObservationDao.search(params); - } catch(InvalidRequestException e) { - assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage()); - } - - // Now let's update - valueSet = new ValueSet(); - valueSet.setId(vsid); - valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A"); - valueSet.setUrl(URL_MY_VALUE_SET); - myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless(); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - params.add(Observation.SP_STATUS, new TokenParam(null, "final")); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - - } - - @Test public void testSearchCodeInExternalCodesystem() { createExternalCsAndLocalVs(); @@ -941,6 +1027,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty()); } + @Test public void testSearchCodeInLocalCodesystem() { createLocalCsAndVs(); @@ -963,6 +1050,78 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } + @Test + public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() { + ValueSet valueSet = new ValueSet(); + valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS"); + valueSet.setUrl(URL_MY_VALUE_SET); + IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params; + + ourLog.info("testSearchCodeInEmptyValueSet without status"); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + try { + myObservationDao.search(params); + } catch(InvalidRequestException e) { + assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage()); + } + + // Now let's update + valueSet = new ValueSet(); + valueSet.setId(vsid); + valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A"); + valueSet.setUrl(URL_MY_VALUE_SET); + myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless(); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + params.add(Observation.SP_STATUS, new TokenParam(null, "final")); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + + } + + /** + * Todo: not yet implemented + */ + @Test + @Ignore + public void testSearchCodeNotInBuiltInValueSet() { + AllergyIntolerance ai1 = new AllergyIntolerance(); + ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE); + String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue(); + + AllergyIntolerance ai2 = new AllergyIntolerance(); + ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED); + String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue(); + + AllergyIntolerance ai3 = new AllergyIntolerance(); + ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE); + String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap params; + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty()); + + // No codes in this one + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3)); + + // Invalid VS + params = new SearchParameterMap(); + params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN)); + try { + myAllergyIntoleranceDao.search(params); + } catch (InvalidRequestException e) { + assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage()); + } + + } + private ArrayList toCodesContains(List theContains) { ArrayList retVal = new ArrayList(); for (ValueSetExpansionContainsComponent next : theContains) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index 7673c4a24a0..a095433946d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { @@ -186,7 +187,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must not be populated")); } @@ -209,7 +210,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must be populated")); } @@ -232,7 +233,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { try { myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd); fail(); - } catch (InvalidRequestException e) { + } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("ID must be populated")); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index fe3db761a81..fe4555ea0a3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -382,6 +382,42 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } } + /** + * Per a message on the mailing list + */ + @Test + public void testTransactionWithPostDoesntUpdate() throws Exception { + + // First bundle (name is Joshua) + + String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8); + Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + Bundle response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id = response.getEntry().get(0).getResponse().getLocation(); + + // Now the second (name is Adam, shouldn't get used) + + input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8); + request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input); + response = mySystemDao.transaction(mySrd, request); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response)); + + assertEquals(1, response.getEntry().size()); + assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus()); + assertEquals("1", response.getEntry().get(0).getResponse().getEtag()); + String id2 = response.getEntry().get(0).getResponse().getLocation(); + assertEquals(id, id2); + + Patient patient = myPatientDao.read(new IdType(id), mySrd); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient)); + assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString()); + } + @Test public void testTransactionCreateInlineMatchUrlWithOneMatch() { String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java index a15ff03eede..811c477e2cb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/SearchParamExtractorDstu3Test.java @@ -60,7 +60,7 @@ public class SearchParamExtractorDstu3Test { } @Override - public Collection getAllSearchParams(String theResourceName) { + public Map> getActiveSearchParams() { throw new UnsupportedOperationException(); } }; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index df6f9ab9680..612adaf421e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -2556,4 +2556,61 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { } + @Test + public void testValidateOnNoId() throws Exception { + HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate"); + CloseableHttpResponse response = ourHttpClient.execute(get); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("No resource supplied for $validate operation")); + assertEquals(400, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + + /** + * From a Skype message from Brian Postlethwaite + */ + @Test + public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception { + + String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"
!-- populated from the rendered HTML below -->
\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service  and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}"; + HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true"); + post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON)); + CloseableHttpResponse response = ourHttpClient.execute(post); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("No issues detected")); + assertEquals(200, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + + /** + * From a Skype message from Brian Postlethwaite + */ + @Test + public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception { + + String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"
!-- populated from the rendered HTML below -->
\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service  and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}"; + HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true"); + post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON)); + CloseableHttpResponse response = ourHttpClient.execute(post); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("Resource has no ID")); + assertEquals(422, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java index 18e2416a292..0f88926f0b9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java @@ -14,6 +14,7 @@ import java.util.Map; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CapabilityStatement; +import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; @@ -35,6 +36,7 @@ import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.rest.gclient.ReferenceClientParam; import ca.uhn.fhir.rest.gclient.TokenClientParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; @@ -70,6 +72,55 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv } } + + @Test + public void testSearchForExtension() { + SearchParameter eyeColourSp = new SearchParameter(); + eyeColourSp.addBase("Patient"); + eyeColourSp.setCode("eyecolour"); + eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); + eyeColourSp.setTitle("Eye Colour"); + eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')"); + eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); + eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); + + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp)); + + ourClient + .create() + .resource(eyeColourSp) + .execute(); + +// mySearchParamRegsitry.forceRefresh(); + + Patient p1 = new Patient(); + p1.setActive(true); + p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue")); + IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless(); + + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p1)); + + Patient p2 = new Patient(); + p2.setActive(true); + p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green")); + IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless(); + + Bundle bundle = ourClient + .search() + .forResource(Patient.class) + .where(new TokenClientParam("eyecolour").exactly().code("blue")) + .returnBundle(Bundle.class) + .execute(); + + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + + List foundResources = toUnqualifiedVersionlessIdValues(bundle); + assertThat(foundResources, contains(p1id.getValue())); + + } + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamDstu3Test.class); + @Override @Before public void beforeResetConfig() { @@ -97,6 +148,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv // Add a custom search parameter SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -107,6 +159,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv // Disable an existing parameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("gender"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("Gender"); @@ -149,6 +202,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv // Add a custom search parameter SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -159,6 +213,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv // Disable an existing parameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("gender"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("Gender"); @@ -203,6 +258,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv public void testSearchWithCustomParam() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -254,6 +310,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv assertEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXED, res.getIndexStatus().longValue()); SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN); fooSp.setTitle("FOO SP"); @@ -274,6 +331,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv public void testSearchQualifiedWithCustomReferenceParam() { SearchParameter fooSp = new SearchParameter(); + fooSp.addBase("Patient"); fooSp.setCode("foo"); fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE); fooSp.setTitle("FOO SP"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java index b62fdd2b2a3..99ccc42b07c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java @@ -6,10 +6,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collection; import org.apache.commons.io.IOUtils; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -181,5 +183,63 @@ public class ResourceProviderQuestionnaireResponseDstu3Test extends BaseResource } + @Test + public void testValidateOnNoId() throws Exception { + HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate"); + CloseableHttpResponse response = ourHttpClient.execute(get); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("No resource supplied for $validate operation")); + assertEquals(400, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + + + /** + * From a Skype message from Brian Postlethwaite + */ + @Test + public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception { + + String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"
!-- populated from the rendered HTML below -->
\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}"; + HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true"); + post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON)); + CloseableHttpResponse response = ourHttpClient.execute(post); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertEquals(200, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + + /** + * From a Skype message from Brian Postlethwaite + */ + @Test + public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception { + + String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"
!-- populated from the rendered HTML below -->
\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}"; + HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true"); + post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON)); + CloseableHttpResponse response = ourHttpClient.execute(post); + try { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertThat(responseString, containsString("Resource has no ID")); + assertEquals(422, response.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(response); + } + + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post1.xml b/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post1.xml new file mode 100644 index 00000000000..745a89effb7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post1.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post2.xml b/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post2.xml new file mode 100644 index 00000000000..c6a60f0258a --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/dstu3-post2.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 4d5b7bc5b01..6056b717e85 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -177,8 +177,14 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + + 1.8 + 1.8 jdk15 test @@ -116,7 +116,7 @@ net.sf.json-lib json-lib - 2.4 + jdk15-sources test @@ -139,7 +139,7 @@ directory-naming naming-java - 0.8 + test diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index 7cbf365e373..ab2c58431df 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -575,6 +575,11 @@ public class SearchDstu2Test { public int size() { return 0; } + + @Override + public String getUuid() { + return null; + } }; } diff --git a/hapi-fhir-structures-dstu3/.editorconfig b/hapi-fhir-structures-dstu3/.editorconfig new file mode 100644 index 00000000000..cd68918bc8b --- /dev/null +++ b/hapi-fhir-structures-dstu3/.editorconfig @@ -0,0 +1,5 @@ +[*.java] +charset = utf-8 +indent_style = space +indent_size = 2 + diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java index b7217351356..40b74e810e2 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java @@ -6,33 +6,33 @@ import java.io.FileNotFoundException; import java.io.IOException; /* -Copyright (c) 2011+, HL7, Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ + * Copyright (c) 2011+, HL7, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ import java.util.ArrayList; import java.util.HashMap; @@ -72,66 +72,67 @@ import org.hl7.fhir.utilities.Utilities; public class ValueSetExpanderSimple implements ValueSetExpander { - private List codes = new ArrayList(); + private List codes = new ArrayList(); private List roots = new ArrayList(); private Map map = new HashMap(); - private IWorkerContext context; + private IWorkerContext context; private boolean canBeHeirarchy = true; - private Set excludeKeys = new HashSet(); + private Set excludeKeys = new HashSet(); private Set excludeSystems = new HashSet(); - private ValueSetExpanderFactory factory; - private ValueSet focus; - private int maxExpansionSize = 500; + private ValueSetExpanderFactory factory; + private ValueSet focus; + private int maxExpansionSize = 500; - private int total; + private int total; - public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) { - super(); - this.context = context; - this.factory = factory; - } + public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) { + super(); + this.context = context; + this.factory = factory; + } - public void setMaxExpansionSize(int theMaxExpansionSize) { - maxExpansionSize = theMaxExpansionSize; - } + public void setMaxExpansionSize(int theMaxExpansionSize) { + maxExpansionSize = theMaxExpansionSize; + } - - private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List designations, ExpansionProfile profile, boolean isAbstract, boolean inactive, List filters) { + private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List designations, + ExpansionProfile profile, boolean isAbstract, boolean inactive, List filters) { if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code)) return null; ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); - n.setSystem(system); - n.setCode(code); - if (isAbstract) + n.setSystem(system); + n.setCode(code); + if (isAbstract) n.setAbstract(true); if (inactive) n.setInactive(true); - + if (profile.getIncludeDesignations() && designations != null) { for (ConceptDefinitionDesignationComponent t : designations) { - ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue()); + ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue()); } } ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null; if (t == null) - n.setDisplay(display); + n.setDisplay(display); else n.setDisplay(t.getValue()); - - String s = key(n); - if (map.containsKey(s) || excludeKeys.contains(s)) { - canBeHeirarchy = false; - } else { - codes.add(n); - map.put(s, n); - } - if (canBeHeirarchy && parent != null) { - parent.getContains().add(n); - } else { - roots.add(n); - } - return n; - } + + String s = key(n); + if (map.containsKey(s) || excludeKeys.contains(s)) { + canBeHeirarchy = false; + } else { + codes.add(n); + map.put(s, n); + total++; + } + if (canBeHeirarchy && parent != null) { + parent.getContains().add(n); + } else { + roots.add(n); + } + return n; + } private boolean filterContainsCode(List filters, String system, String code) { for (ValueSet vse : filters) @@ -160,93 +161,92 @@ public class ValueSetExpanderSimple implements ValueSetExpander { return null; } - private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List filters) throws FHIRException { - if (!CodeSystemUtilities.isDeprecated(cs, def)) { - ValueSetExpansionContainsComponent np = null; + private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List filters) + throws FHIRException { + if (!CodeSystemUtilities.isDeprecated(cs, def)) { + ValueSetExpansionContainsComponent np = null; boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); - boolean inc = CodeSystemUtilities.isInactive(cs, def); + boolean inc = CodeSystemUtilities.isInactive(cs, def); if (canBeHeirarchy || !abs) np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters); for (ConceptDefinitionComponent c : def.getConcept()) addCodeAndDescendents(cs, system, c, np, profile, filters); - } else - for (ConceptDefinitionComponent c : def.getConcept()) - addCodeAndDescendents(cs, system, c, null, profile, filters); + } else + for (ConceptDefinitionComponent c : def.getConcept()) + addCodeAndDescendents(cs, system, c, null, profile, filters); - } + } private void addCodes(ValueSetExpansionComponent expand, List params, ExpansionProfile profile, List filters) throws ETooCostly { - if (expand.getContains().size() > maxExpansionSize) - throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); - for (ValueSetExpansionParameterComponent p : expand.getParameter()) { - if (!existsInParams(params, p.getName(), p.getValue())) - params.add(p); - } + if (expand.getContains().size() > maxExpansionSize) + throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); + for (ValueSetExpansionParameterComponent p : expand.getParameter()) { + if (!existsInParams(params, p.getName(), p.getValue())) + params.add(p); + } copyImportContains(expand.getContains(), null, profile, filters); - - total = expand.getTotal(); - } + } - private void excludeCode(String theSystem, String theCode) { - ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); - n.setSystem(theSystem); - n.setCode(theCode); - String s = key(n); - excludeKeys.add(s); - } + private void excludeCode(String theSystem, String theCode) { + ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); + n.setSystem(theSystem); + n.setCode(theCode); + String s = key(n); + excludeKeys.add(s); + } - private void excludeCodes(ConceptSetComponent exc, List params) throws TerminologyServiceException { - if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) { - excludeSystems.add(exc.getSystem()); - } + private void excludeCodes(ConceptSetComponent exc, List params) throws TerminologyServiceException { + if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) { + excludeSystems.add(exc.getSystem()); + } if (exc.hasValueSet()) throw new Error("Processing Value set references in exclude is not yet done"); -// importValueSet(imp.getValue(), params, profile); + // importValueSet(imp.getValue(), params, profile); - CodeSystem cs = context.fetchCodeSystem(exc.getSystem()); - if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) { - excludeCodes(context.expandVS(exc, false), params); - return; - } + CodeSystem cs = context.fetchCodeSystem(exc.getSystem()); + if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) { + excludeCodes(context.expandVS(exc, false), params); + return; + } - for (ConceptReferenceComponent c : exc.getConcept()) { - excludeCode(exc.getSystem(), c.getCode()); - } - - if (exc.getFilter().size() > 0) - throw new NotImplementedException("not done yet"); - } + for (ConceptReferenceComponent c : exc.getConcept()) { + excludeCode(exc.getSystem(), c.getCode()); + } - private void excludeCodes(ValueSetExpansionComponent expand, List params) { - for (ValueSetExpansionContainsComponent c : expand.getContains()) { - excludeCode(c.getSystem(), c.getCode()); - } - } + if (exc.getFilter().size() > 0) + throw new NotImplementedException("not done yet"); + } - private boolean existsInParams(List params, String name, Type value) { - for (ValueSetExpansionParameterComponent p : params) { - if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false)) - return true; - } - return false; - } + private void excludeCodes(ValueSetExpansionComponent expand, List params) { + for (ValueSetExpansionContainsComponent c : expand.getContains()) { + excludeCode(c.getSystem(), c.getCode()); + } + } - @Override + private boolean existsInParams(List params, String name, Type value) { + for (ValueSetExpansionParameterComponent p : params) { + if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false)) + return true; + } + return false; + } + + @Override public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) { if (profile == null) profile = makeDefaultExpansion(); - try { - focus = source.copy(); - focus.setExpansion(new ValueSet.ValueSetExpansionComponent()); - focus.getExpansion().setTimestampElement(DateTimeType.now()); - focus.getExpansion().setIdentifier(Factory.createUUID()); + try { + focus = source.copy(); + focus.setExpansion(new ValueSet.ValueSetExpansionComponent()); + focus.getExpansion().setTimestampElement(DateTimeType.now()); + focus.getExpansion().setIdentifier(Factory.createUUID()); if (!profile.getUrl().startsWith("urn:uuid:")) focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl())); - if (source.hasCompose()) + if (source.hasCompose()) handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile); if (canBeHeirarchy) { @@ -254,38 +254,38 @@ public class ValueSetExpanderSimple implements ValueSetExpander { focus.getExpansion().getContains().add(c); } } else { - for (ValueSetExpansionContainsComponent c : codes) { + for (ValueSetExpansionContainsComponent c : codes) { if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them - focus.getExpansion().getContains().add(c); + focus.getExpansion().getContains().add(c); c.getContains().clear(); // make sure any heirarchy is wiped } - } - } - - if (total > 0) { - focus.getExpansion().setTotal(total); - } - + } + } + + if (total > 0) { + focus.getExpansion().setTotal(total); + } + return new ValueSetExpansionOutcome(focus); - } catch (RuntimeException e) { - // TODO: we should put something more specific instead of just Exception below, since - // it swallows bugs.. what would be expected to be caught there? - throw e; + } catch (RuntimeException e) { + // TODO: we should put something more specific instead of just Exception below, since + // it swallows bugs.. what would be expected to be caught there? + throw e; } catch (NoTerminologyServiceException e) { // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set // that might fail too, but it might not, later. return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE); - } catch (Exception e) { - // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set - // that might fail too, but it might not, later. - return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); - } - } + } catch (Exception e) { + // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set + // that might fail too, but it might not, later. + return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); + } + } - private ExpansionProfile makeDefaultExpansion() { - ExpansionProfile res = new ExpansionProfile(); - res.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase()); - res.setExcludeNested(true); + private ExpansionProfile makeDefaultExpansion() { + ExpansionProfile res = new ExpansionProfile(); + res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase()); + res.setExcludeNested(true); res.setIncludeDesignations(false); return res; } @@ -296,68 +296,70 @@ public class ValueSetExpanderSimple implements ValueSetExpander { } } - private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException { - ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code); - if (def == null) - throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl()); - return def.getDisplay(); - } + private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException { + ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code); + if (def == null) + throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl()); + return def.getDisplay(); + } - private ConceptDefinitionComponent getConceptForCode(List clist, String code) { - for (ConceptDefinitionComponent c : clist) { - if (code.equals(c.getCode())) - return c; - ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code); - if (v != null) - return v; - } - return null; - } + private ConceptDefinitionComponent getConceptForCode(List clist, String code) { + for (ConceptDefinitionComponent c : clist) { + if (code.equals(c.getCode())) + return c; + ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code); + if (v != null) + return v; + } + return null; + } - private void handleCompose(ValueSetComposeComponent compose, List params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException { - // Exclude comes first because we build up a map of things to exclude - for (ConceptSetComponent inc : compose.getExclude()) - excludeCodes(inc, params); + private void handleCompose(ValueSetComposeComponent compose, List params, ExpansionProfile profile) + throws ETooCostly, FileNotFoundException, IOException, FHIRException { + // Exclude comes first because we build up a map of things to exclude + for (ConceptSetComponent inc : compose.getExclude()) + excludeCodes(inc, params); canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty(); - boolean first = true; - for (ConceptSetComponent inc : compose.getInclude()) { - if (first == true) - first = false; - else - canBeHeirarchy = false; + boolean first = true; + for (ConceptSetComponent inc : compose.getInclude()) { + if (first == true) + first = false; + else + canBeHeirarchy = false; includeCodes(inc, params, profile); - } + } - } + } - private ValueSet importValueSet(String value, List params, ExpansionProfile profile) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException { - if (value == null) - throw new TerminologyServiceException("unable to find value set with no identity"); - ValueSet vs = context.fetchResource(ValueSet.class, value); - if (vs == null) - throw new TerminologyServiceException("Unable to find imported value set " + value); - ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile); + private ValueSet importValueSet(String value, List params, ExpansionProfile profile) + throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException { + if (value == null) + throw new TerminologyServiceException("unable to find value set with no identity"); + ValueSet vs = context.fetchResource(ValueSet.class, value); + if (vs == null) + throw new TerminologyServiceException("Unable to find imported value set " + value); + ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile); if (vso.getError() != null) - throw new TerminologyServiceException("Unable to expand imported value set: "+vso.getError()); - if (vso.getService() != null) - throw new TerminologyServiceException("Unable to expand imported value set "+value); - if (vs.hasVersion()) - if (!existsInParams(params, "version", new UriType(vs.getUrl()+"|"+vs.getVersion()))) - params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl()+"|"+vs.getVersion()))); - for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { - if (!existsInParams(params, p.getName(), p.getValue())) - params.add(p); - } + throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError()); + if (vso.getService() != null) + throw new TerminologyServiceException("Unable to expand imported value set " + value); + if (vs.hasVersion()) + if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion()))) + params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion()))); + for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { + if (!existsInParams(params, p.getName(), p.getValue())) + params.add(p); + } canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy - return vso.getValueset(); + return vso.getValueset(); } private void copyImportContains(List list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List filter) { for (ValueSetExpansionContainsComponent c : list) { ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter); copyImportContains(c.getContains(), np, profile, filter); - } - } + } + } private void includeCodes(ConceptSetComponent inc, List params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException { List imports = new ArrayList(); @@ -367,9 +369,9 @@ public class ValueSetExpanderSimple implements ValueSetExpander { if (!inc.hasSystem()) { if (imports.isEmpty()) // though this is not supposed to be the case return; - ValueSet base = imports.get(0); + ValueSet base = imports.get(0); imports.remove(0); - copyImportContains(base.getExpansion().getContains(), null, profile, imports); + copyImportContains(base.getExpansion().getContains(), null, profile, imports); } else { CodeSystem cs = context.fetchCodeSystem(inc.getSystem()); if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) { @@ -379,15 +381,15 @@ public class ValueSetExpanderSimple implements ValueSetExpander { if (cs == null) { if (context.isNoTerminologyServer()) - throw new NoTerminologyServiceException("unable to find code system "+inc.getSystem().toString()); + throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString()); else - throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString()); + throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString()); } if (cs.getContent() != CodeSystemContentMode.COMPLETE) - throw new TerminologyServiceException("Code system "+inc.getSystem().toString()+" is incomplete"); + throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete"); if (cs.hasVersion()) - if (!existsInParams(params, "version", new UriType(cs.getUrl()+"|"+cs.getVersion()))) - params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl()+"|"+cs.getVersion()))); + if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion()))) + params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion()))); if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { // special case - add all the code system @@ -399,7 +401,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander { if (!inc.getConcept().isEmpty()) { canBeHeirarchy = false; for (ConceptReferenceComponent c : inc.getConcept()) { - addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false, CodeSystemUtilities.isInactive(cs, c.getCode()), imports); + addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false, + CodeSystemUtilities.isInactive(cs, c.getCode()), imports); } } if (inc.getFilter().size() > 1) { @@ -412,13 +415,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander { // special: all codes in the target code system under the value ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); if (def == null) - throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'"); + throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports); } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) { // special: all codes in the target code system under the value ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); if (def == null) - throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'"); + throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); for (ConceptDefinitionComponent c : def.getConcept()) addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports); } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) { @@ -428,7 +431,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander { if (def != null) { if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) { if (def.getDisplay().contains(fc.getValue())) { - addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), imports); + addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), + imports); } } } @@ -449,12 +453,12 @@ public class ValueSetExpanderSimple implements ValueSetExpander { return res; } - private String key(String uri, String code) { - return "{" + uri + "}" + code; - } + private String key(String uri, String code) { + return "{" + uri + "}" + code; + } - private String key(ValueSetExpansionContainsComponent c) { - return key(c.getSystem(), c.getCode()); - } + private String key(ValueSetExpansionContainsComponent c) { + return key(c.getSystem(), c.getCode()); + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java index 7b50cd0a1be..e6f79e9f0a0 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java @@ -1,8 +1,7 @@ package ca.uhn.fhir.rest.server; -import static org.hamcrest.Matchers.either; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.oneOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -65,7 +64,7 @@ public class SearchDefaultMethodDstu3Test { ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(ourLastMethod, either(equalTo("search01")).or(equalTo("search02"))); + assertThat(ourLastMethod, oneOf("search01", "search02", "search03")); assertEquals(null, ourLastParam1); assertEquals(null, ourLastParam2); assertEquals(null, ourLastAdditionalParams); diff --git a/hapi-fhir-structures-hl7org-dstu2/.editorconfig b/hapi-fhir-structures-hl7org-dstu2/.editorconfig new file mode 100644 index 00000000000..cd68918bc8b --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/.editorconfig @@ -0,0 +1,5 @@ +[*.java] +charset = utf-8 +indent_style = space +indent_size = 2 + diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index c04497c465c..95a218e25ac 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -121,7 +121,7 @@ net.sf.json-lib json-lib - 2.4 + jdk15 test @@ -142,7 +142,7 @@ net.sf.json-lib json-lib - 2.4 + jdk15-sources test @@ -165,7 +165,7 @@ directory-naming naming-java - 0.8 + test diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java index e00fe717dbf..3fa8347a033 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java @@ -152,6 +152,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid throw new ConfigurationException(e); } + v.setShouldCheckForIdPresence(false); v.setBestPracticeWarningLevel(myBestPracticeWarningLevel); v.setAnyExtensionsAllowed(true); v.setRequireResourceId(false); diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java index 8c453d9900c..53d6af47775 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java @@ -80,7 +80,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat // used during the build process to keep the overall volume of messages down private boolean suppressLoincSnomedMessages; - public InstanceValidator(IWorkerContext theContext) throws Exception { + private boolean shouldCheckForIdPresence = true; + +public InstanceValidator(IWorkerContext theContext) throws Exception { super(); this.context = theContext; source = Source.InstanceValidator; @@ -117,6 +119,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + public void setShouldCheckForIdPresence(boolean theShouldCheckForIdPresence) { + shouldCheckForIdPresence = theShouldCheckForIdPresence; + } + private boolean check(String v1, String v2) { return v1 == null ? Utilities.noString(v1) : v1.equals(v2); } @@ -1825,7 +1831,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (type.equals("Extension")) checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack); else if (type.equals("Resource")) - validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path)); // if + validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path) && shouldCheckForIdPresence); // if // (str.matches(".*([.,/])work\\1$")) else { StructureDefinition p = getProfileForType(type); diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java index 57be98b0552..a5170173a49 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java @@ -160,6 +160,11 @@ public class SearchHl7OrgDstu2Test { public int size() { return 0; } + + @Override + public String getUuid() { + return null; + } }; } diff --git a/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 60105c1b951..00000000000 --- a/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 722289db81a..f98aedbab11 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -1,3486 +1,3512 @@ - - - - James Agnew - HAPI FHIR Changelog - - - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Hibernate (JPA): 5.1.0 -> 5.2.7
  • -
  • Hibernate Search (JPA): 5.5.4 ->p; 5.7.0.CR1
  • -
  • Hibernate Validator (JPA): 5.2.4 ->p; 5.3.4
  • -
  • Spring (JPA): 4.3.1 -> 4.3.6
  • - - ]]> -
    - - The JPA server now supports custom search parameters in DSTU3 - mode. This allows users to create search parameters which contain - custom paths, or even override and disable existing search - parameters. - - CLI example uploader couldn't find STU3 examples after CI server - was moved to build.fhir.org - - - Fix issue in JPA subscription module that prevented purging stale - subscriptions when many were present on Postgres - - - Server interceptor methods were being called twice unnecessarily - by the JPA server, and the DaoConfig interceptor registration - framework was not actually useful. Thanks to GitHub user - @mattiuusitalo for reporting! - - - AuthorizationInterceptor on JPA server did not correctly - apply rules on deleting resources in a specific compartment - because the resource metadata was stripped by the JPA server - before the interceptor could see it. Thanks to - Eeva Turkka for reporting! - - - JPA server exported CapabilityStatement includes - double entries for the _id parameter and uses the - wrong type (string instead of token). Thanks to - Robert Lichtenberger for reporting! - - - Custom resource types which extend Binary must not - have declared extensions since this is invalid in - FHIR (and HAPI would just ignore them anyhow). Thanks - to Thomas S Berg for reporting! - - - Standard HAPI zip/tar distributions did not include the project - sources and JavaDoc JARs. Thanks to Keith Boone for pointing - this out! - - - Server AuthorizationInterceptor always rejects history operation - at the type level even if rules should allow it. - - - JPA server terminology service was not correctly validating or expanding codes - in SNOMED CT or LOINC code systems. Thanks to David Hay for reporting! - - - Attempting to search for an invalid resource type (e.g. GET base/FooResource) should - return an HTTP 404 and not a 400, per the HTTP spec. Thanks to - GitHub user @CarthageKing for the pull request! - - - When parsing a Bundle containing placeholder fullUrls and references - (e.g. "urn:uuid:0000-0000") the resource reference targets did not get - populated with the given resources. Note that as a part of this - change, IdType
    and IdDt]]> have been modified - so that when parsing a placeholder ID, the complete placeholder including the - "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, - the prefix was treated as the base URL, which led to strange behaviour - like the placeholder being treated as a real IDs. Thanks to GitHub - user @jodue for reporting! - - - Declared extensions with multiple type() options listed in the @Child - annotation caused a crash on startup. Now this is supported. - - - STU3 XHTML parser for narrative choked if the narrative contained - an &rsquot;]]> entity string. - - - When parsing a quantity parameter on the server with a - value and units but no system (e.g. - GET [base]/Observation?value=5.4||mg]]>) - the unit was incorrectly treated as the system. Thanks to - @CarthageKing for the pull request! - - - Correct a typo in the JPA ValueSet ResourceProvider which prevented - successful operation under Spring 4.3. Thanks to - Robbert van Waveren for the pull request! - - - Deprecate the method - ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> - as it is no longer used by HAPI and is just an annoying step - in creating custom structures. Thanks to Allan Bro Hansen - for pointing this out. - - - CapturingInterceptor did not buffer the response meaning - that in many circumstances it did not actually capture - the response. Thanks to Jenny Syed of Cerner for - the pull request and contribution! - - - Clean up dependencies and remove Eclipse project files from git. Thanks to - @sekaijin for the pull request! - - - Client revincludes did not include the :recurse modifier. Thanks to - Jenny Meinsma for pointing this out on Zulip! - - - JPA server did not return an OperationOutcome in the response for - a normal delete operation. - - - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - - -
  • Derby (CLI): 10.12.1.1 -> 10.13.1.1
  • -
  • Jetty (CLI): 9.3.10.v20160621 -> 9.3.14.v20161028
  • -
  • JAnsi (CLI): 1.13 -> 1.14
  • -
  • Phloc Commons (SCH Validator): 4.4.5 -> 4.4.6
  • - - ]]> -
    - - Fix issue in AuthorizationIntetceptor where - transactions are blocked even when they - should not be - - - Fix regression in HAPI FHIR 2.1 JPA - server where some search parameters on - metadata resources did not appear - (e.g. "StructureDefinition.url"). Thanks - to David Hay for reporting! - - - Add ability to JPA server for disabling stale search - expiry. This is useful if you are deploying the server - to a cluster. - - - RestfulServer with no explicitly set FhirContext - fails to detect the presents of DSTU3 structures. Thanks - to GitHub user @vijayt27 for reporting! - - - As the - eBay CORS interceptor]]> - project - has gone dormant, we have introduced a new - HAPI server interceptor which can be used to implement CORS support - instead of using the previously recommended Servlet Filter. All server - examples as well as the CLI have been switched to use this new interceptor. - See the - CORS Documentation]]> - for more information. - - - Make the parser configurable so that when - parsing an invalid empty value (e.g. - {"status":""}]]>) the - parser will either throw a meaningful exception - or log a warning depending on the configured - error handler. - - - Fix issue when serializing resources that have - contained resources which are referred to - from multiple places. Sometimes when serializing - these resources the contained resource section - would contain duplicates. Thanks to Hugo Soares - and Stefan Evinance for reporting and providing - a test case! - - - Allow client to gracefully handle running in DSTU3 mode - but with a structures JAR that does not contain a - CapabilityStatement resource. Thanks to Michael Lawley - for the pull request! - - - Fix a crash in JPA server when searching using an _include if _include targets are - external references (and therefore can't be loaded - by the server). Thanks to Hannes Ulrich for reporting! - - - HAPI FHIR CLI failed to delete a file when uploading - example resources while running under Windows. - - - Server should reject update if the resource body - does not contain an ID, or the ID does not match - the request URL. Thanks to Jim Steel for reporting! - - - Web Testing UI's next and previous buttons for paging - through paged results did not work after the migration - to using Thymeleaf 3. Thanks to GitHub user @gsureshkumar - for reporting! - - - When parsing invalid enum values in STU3, - report errors through the parserErrorHandler, - not by throwing an exception. Thanks to - Michael Lawley for the pull request! - - - When parsing DSTU3 resources with enumerated - types that contain invalid values, the parser will now - invoke the parserErrorHandler. For example, when parsing - {"resourceType":"Patient", "gender":"foo"} - ]]> - the previous behaviour was to throw an InvalidArgumentException. - Now, the parserErrorHandler is invoked. In addition, thw - LenientErrorHandler has been modified so that this one case - will result in a DataFormatException. This has the effect - that servers which receive an invalid enum velue will return - an HTTP 400 instead of an HTTP 500. Thanks to Jim - Steel for reporting! - - - DSTU3 context now pulls the FHIR version from the actual - model classes. Thanks to Michael Lawley for the pull request! - - - Enhancements to the tinder-plugin's generic template features - of the generate-multi-files and generate-single-file - Maven goals as well as the Ant hapi-tinder task. -
      -
    • Provides the full Tinder data model by adding composites, valuesets, and profiles to resourcesw.
    • -
    • Supports generating files for resources, composites, valuesets, and profiles
    • -
    • Supports Velocimacro files outside the tinder-plugin JAR
    • -
    • Provides filename prefix as well as suffix properties
    • -
    • Can specify any of the Velocity configuration parameters such as - macro.provide.scope.control which allows safe macro recursion
    • -
    • Templates can now drill down into the referenced children for a ResourceBlockCopy
    • -
    • Normalization of properties across all three generic tasks
    • -
    - ]]> -
    - - Fix ordering of validator property handling when an element - has a name that is similar to a shorter name[x] style name. - Thanks to CarthageKing for the pull request! - - - Add a docker configuration to the hapi-fhir-jpaservr-example - module. Thanks to Gijsbert van den Brink for the pull request! - - - Add utility constructors to MoneyDt. Thanks to James Ren for the - contribution! - - - AuthorizationInterceptor was failing to allow read requests to pass - when a rule authorized those resources by compartment. Thanks to - GitHub user @mattiuusitalo for reporting and supplying - a test case! - - - Correct a typo in client - IHttpRequest]]> class: "bufferEntitity" should be "bufferEntity". - - - ErrorHandler is now called (resulting in a warning by default, but can also be an exception) when arsing JSON if - the resource ID is not a JSON string, or an object is found where an array is expected (e.g. repeating field). Thanks - to Jenni Syed of Cerner for providing a test case! - - - Fix Web Testing UI to be able to handle STU3 servers which - return CapabilityStatement instead of the previously used - "Conformance" resource - - - CLI example uploader couldn't find STU3 examples after CI server - was moved to build.fhir.org - - - Fix issue in JPA subscription module that prevented purging stale - subscriptions when many were present on Postgres - - - Server interceptor methods were being called twice unnecessarily - by the JPA server, and the DaoConfig interceptor registration - framework was not actually useful. Thanks to GitHub user - @mattiuusitalo for reporting! - - - AuthorizationInterceptor on JPA server did not correctly - apply rules on deleting resources in a specific compartment - because the resource metadata was stripped by the JPA server - before the interceptor could see it. Thanks to - Eeva Turkka for reporting! - - - JPA server exported CapabilityStatement includes - double entries for the _id parameter and uses the - wrong type (string instead of token). Thanks to - Robert Lichtenberger for reporting! - - - Custom resource types which extend Binary must not - have declared extensions since this is invalid in - FHIR (and HAPI would just ignore them anyhow). Thanks - to Thomas S Berg for reporting! - - - Standard HAPI zip/tar distributions did not include the project - sources and JavaDoc JARs. Thanks to Keith Boone for pointing - this out! - - - Server AuthorizationInterceptor always rejects history operation - at the type level even if rules should allow it. - - - JPA server terminology service was not correctly validating or expanding codes - in SNOMED CT or LOINC code systems. Thanks to David Hay for reporting! - - - Attempting to search for an invalid resource type (e.g. GET base/FooResource) should - return an HTTP 404 and not a 400, per the HTTP spec. Thanks to - GitHub user @CarthageKing for the pull request! - - - When parsing a Bundle containing placeholder fullUrls and references - (e.g. "urn:uuid:0000-0000") the resource reference targets did not get - populated with the given resources. Note that as a part of this - change, IdType and IdDt]]> have been modified - so that when parsing a placeholder ID, the complete placeholder including the - "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, - the prefix was treated as the base URL, which led to strange behaviour - like the placeholder being treated as a real IDs. Thanks to GitHub - user @jodue for reporting! - - - Declared extensions with multiple type() options listed in the @Child - annotation caused a crash on startup. Now this is supported. - - - STU3 XHTML parser for narrative choked if the narrative contained - an &rsquot;]]> entity string. - - - When parsing a quantity parameter on the server with a - value and units but no system (e.g. - GET [base]/Observation?value=5.4||mg]]>) - the unit was incorrectly treated as the system. Thanks to - @CarthageKing for the pull request! - - - Correct a typo in the JPA ValueSet ResourceProvider which prevented - successful operation under Spring 4.3. Thanks to - Robbert van Waveren for the pull request! - - - Deprecate the method - ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> - as it is no longer used by HAPI and is just an annoying step - in creating custom structures. Thanks to Allan Bro Hansen - for pointing this out. - - - CapturingInterceptor did not buffer the response meaning - that in many circumstances it did not actually capture - the response. Thanks to Jenny Syed of Cerner for - the pull request and contribution! - -
    - - - STU3 structure definitions have been updated to the - STU3 latest definitions (1.7.0 - SVN 10129). In - particular, this version supports the new CapabilityStatement - resource which replaces the previous Conformance - resource (in order to reduce upgrade pain, both resource - types are included in this version of HAPI) - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • spring-data-orm (JPA): 1.10.2 -> 1.10.4
  • - - ]]> -
    - - Fix a fairly significant issue in JPA Server when using the DatabaseBackedPagingProvider]]>: When paging over the results - of a search / $everything operation, under certain circumstances resources may be missing from the last page of results - that is returned. Thanks to David Hay for reporting! - - - Client, Server, and JPA server now support experimental support - for - - using the XML Patch and JSON Patch syntax as explored during the - September 2016 Baltimore Connectathon. See - this wiki page]]> - for a description of the syntax. - ]]> - Thanks to Pater Girard for all of his help during the connectathon - in implementing this feature! - - - Android library now uses OkHttp client by default instead - of Apache HttpClient. This should lead to much simpler - support for Android in the future. - - - Both client and server now use the new STU3 mime types by default - if running in STU3 mode (in other words, using an STU3 - FhirContext). - - - In server, when returning a list of resources, the server sometimes failed to add - _include]]> resources to the response bundle if they were - referred to by a contained reosurce. Thanks to Neal Acharya for reporting! - - - Fix regression in web testing UI where "prev" and "next" buttons don't work - when showing a result bundle - - - JPA server should not attempt to resolve built-in FHIR StructureDefinitions from the - database (this causes a significant performance hit when validating) - - - BanUnsupportedHttpMethodsInterceptor was erroring out when a client - attempts HTTP HEAD requests - - - Conditional URLs in JPA server (e.g. for delete or update) did not support the - _has]]> parameter - - - Remove Maven dependency on Saxon library, as it is not actually used. Thanks - to Lem Edmondson for the suggestion! - - - Times before 1970 with fractional milliseconds were parsed incorrectly. Thanks - to GitHub user @CarthageKing for reporting! - - - Prevent crash in parser when parsing resource - with multiple profile declarations when - default type for profile is used. Thanks to - Filip Domazet for the pull request! - - - STU3 servers were adding the old MimeType - strings to the - Conformance.format]]> - part of the generated server conformance - statement - - - When performing an update using the client on a resource that - contains other resources (e.g. Bundle update), all child resources in the - parent bundle were incorrectly given the ID of the parent. Thanks - to Filip Domazet for reporting! - - - STU clients now use an Accept header which - indicates support for both the old MimeTypes - (e.g. application/xml+fhir]]>) - and the new MimeTypes - (e.g. application/fhir+xml]]>) - - - JPA server now sends correct - HTTP 409 Version Conflict]]> - when a - DELETE fails because of constraint issues, instead of - HTTP 400 Invalid Request]]> - - - Server history operation did not populate the Bundle.entry.request.url - field, which is required in order for the bundle to pass validation. - Thanks to Richard Ettema for spotting this! - - - Add a new method to the server interceptor framework which will be - called after all other processing is complete (useful for performance - tracking). The server LoggingInterceptor has been switched to using this - method which means that log lines will be created when processing is finished, - instead of when it started. - - - STU3 clients were not sending the new mimetype values in the - Content-Type]]> header. Thanks to - Claude Nanjo for pointing this out! - - - JAX-RS server was not able to handle the new mime types defined - in STU3 - - - JPA server did not handle custom types when being called - programatically (I.e. not through HTTP interface). Thanks to - Anthony Mei for pointing this out! - - - CLI was not correctly able to upload DSTU2 examples to any server - - - STU3 validator has been upgrated to include fixes made since the - 1.6.0 ballot - - - Prevent JPA server from creating a bunch of - FhirContext objects for versions of FHIR that - aren't actually being used - - - XhtmlNode.equalsDeep() contained a bug which caused resources - containing a narrative to always return - false]]> for STU3 - Resource#equalsDeep()]]>. Thanks to - GitHub user @XcrigX for reporting! - - - JPA server did not correctly process searches for chained parameters - where the chain passed across a field that was a choice between a - reference and a non-reference type (e.g. - MedicationAdministration.medication[x]]]>. - Thanks to GitHub user @Crudelus for reporting! - - - Handle parsing an extension without a URL more gracefully. In HAPI FHIR 2.0 this caused - a NullPointerException to be thrown. Now it will trigger a warning, or throw a - DataFormatException if the StrictErrorHandler is configured on the parser. - - - Calling a HAPI server URL with a chain on a parameter that shouldn't accept - chains (e.g. - GET [base]/Patient?name.foo=smith]]>) - did not return an error and instead just ignored the chained part - and treated the parameter as though it did not have the chain. This - led to confusing and potentially unsafe behaviour. This has been - corrected to return an error to the client. Thanks to - Kevin Tallevi for finding this! - - - Fix #411 - Searching by POST [base]/_search]]> with urlencoded parameters doesn't work correctly if - interceptors are accessing the parameters and there is are also - parameters on the URL. Thanks to Jim Steel for reporting! - - - Fluent client can now return types other than Parameters - when invoking operations. - - - JPA server shouldn't report a totalCount in Bundle of "-1" when - there are no results - - - JPA server was not correctly normalizing strings with non-latin characters - (e.g. Chinese chars). Thanks to GitHub user @YinAqu for reporting and providing - some great analysis of the issue! - - - Add a new method to ReferenceClientParam which allows you to - pass in a number of IDs by a collection of Strings. Thanks to - Thomas Andersen for the pul request! - - - When encoding a resource in JSON where the resource has - an extension with a value where the value is a reference to a - contained resource, the reference value (e.g. "#1") did not - get serialized. Thanks to GitHub user @fw060 for reporting! - - - ResponseHighlighterInterceptor now pretty-prints responses - by default unless the user has explicitly requested - a non-pretty-printed response (ie. - using ?_pretty=false]]>. Thanks to - Allan Brohansen and Jens Villadsen for the suggestion! - - - Add a new JSON library abstraction layer to the JSON parser. - This contribution shouldn't have any end-user impact but does - make it easier to use the JSON parser to generate custom structures - for other purposes, and should allow us to support RDF more - easily at some point. Thanks to Bill Denton for the pull - request and the contribution! - - - DSTU1 Bundle encoder did not include the Bundle entry author in - the generated bundle. Thanks to Hannes Venter for the pull - request and contribution! - - - Remove unused field (myIsContained) from ResourceTable - in JPA server. - - - AuthorizationInterceptor is now a bit more aggressive - at blocking read operations, stopping them on the - way in if there is no way they will be accepted - to the resource check on the way out. In addition - it can now be configured to allow/deny operation - invocations at the instance level on any - instance of a given type - - - STU3 servers were incorrectly returning the - Content-Location]]> - header instead of the - Content]]> - header. The former has been removed from the - FHIR specification in STU3, but the - latter got removed in HAPI's code base. - Thanks to Jim Steel for reporting! - - - Correct several documentation issues. Thanks to Vadim Peretokin - for the pull requests! - - - Remove an unneccesary database flush - from JPA persistence operations - - - Add method to fluent client to allow OR search across several - profiles. Thanks to Thomas Andersen for the pull request! - -
    - - - JSON parsing in HAPI FHIR has been switched from using JSR353 (javax.json) to - using Google Gson. For this reason we are bumping the major release number to - 2.0. Theoretically this should not affect projects in any major way, but Gson - does have subtle differences. Two differences which popped up a fair bit in - our own testing: - -
      - A space is placed after the : in keys, e.g. what was previously - encoded as "resourceType":"Patient" is now encoded - as "resourceType": "Patient" (this broke a number of - our unit tests with hardcoded resource definitions) -
    -
      - Trailing content after a valid json resource is rejected by - Gson (it was ignored by the Glassfish parser we were previously - using even though it was invalid) -
    - - ]]> -
    - - STU3 structure definitions have been updated to the - STU3 ballot candidate versions (1.6.0 - SVN 9663) - - - Both client and server now support the new Content Types decided in - FHIR #10199]]>. -
    ]]> - This means that the server now supports - application/fhir+xml and application/fhir+json]]> - in addition to the older style - application/xml+fhir and application/json+fhir]]>. - In order to facilitate migration by implementors, the old style remains the default - for now, but the server will respond using the new style if the request contains it. The - client now uses an Accept]]> header value which requests both - styles with a preference given to the new style when running in DSTU3 mode. -
    ]]> - As a part of this change, the server has also been enhanced so that if a request - contains a Content-Type header but no Accept header, the response will prefer the - encoding specified by the Content-Type header. -
    - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Logback (used in sample projects): 1.1.5 -> 1.1.7
  • -
  • Phloc Commons (used by schematron validator): 4.4.4 -> 4.4.5
  • -
  • Commons-IO: 2.4 -> 2.5
  • -
  • Apache HTTPClient: 4.5.1 -> 4.5.2
  • -
  • Apache HTTPCore: 4.4.4 -> 4.4.5
  • -
  • Jersey (JAX-RS tests): 2.22.2 -> 2.23.1
  • -
  • Spring (JPA, Web Tester): 4.3.0 -> 4.3.1
  • - -
  • Hibernate Search (JPA): 5.5.2 -> 5.5.4
  • -
  • Thymeleaf (Narrative Generator / Web Tester): 2.1.4 ->3.0.1
  • - - ]]> -
    - - - Fix issue in DSTU1 Bundle parsing where unexpected elements in the bundle resulted in a failure - to parse. - - - DSTU2 QuestionnaireResponse validator failed with an exception if the - QuestionnaireResponse contained certain groups with no content - - - Fluent client should ignore parameter values which are null instead of including - them as ?foo=null]]> - - - When using _elements]]> parameter on server, the server was not - automatically adding the SUBSETTED]]> tag as it should - - - JPA server should now automatically detect - if Hibernate Search (Lucene) is configured to be - disabled and will not attempt to use it. This - prevents a crash for some operations. - - - A new server interceptor "BanUnsupprtedHttpMethodsInterceptor" has been added - which causes the server to return an HTTP 405 if an unsupported HTTP - verb is received from the client - - - Fix an issue where resource IDs were not correctly set when using - DSTU2 HL7org structures with the JAX-RS module. Thanks to Carlo Mion - for the pull request! - - - hapi-fhir-testpage-overlay project contained an unneccesary - dependency on hapi-fhir-jpaserver-base module, which resulted in - projects using the overlay having a large number of unnneded - JARs included - - - It is not possible to configure both the parser and the context to - preserve versions in resource references (default behaviour is to - strip versions from references). Thanks to GitHub user @cknaap - for the suggestion! - - - Tag#setCode(String)]]> did not actually set the code it was supposed to - set. Thanks to Tim Tschampel for reporting! - - - JPA server's /Bundle]]> endpoint cleared - the Bundle.entry.fullUrl]]> field on stored - bundles, resulting in invalid content being saved. Thanks to Mirjam - Baltus for reporting! - - - JPA server now returns HTTP 200 instead of HTTP 404 for - conditional deletes which did not find any matches, - per FHIR-I decision. - - - Client that declares explicitly that it is searching/reading/etc for - a custom type did not automatically parse into that type. - - - Allow servers to specify the authentication realm of their choosing when - throwing an AuthenticationException. Thanks to GitHub user @allanbrohansen - for the suggestion! - - - Add a new client implementation which uses the - OkHttp]]> - library as the HTTP client implementation (instead of Apache HttpClient). - This is particularly useful for Android (where HttpClient is a pain) but - could also be useful in other places too. - Thanks to Matt Clarke of Orion Health for the contribution! - - - Fix a regression when parsing resources that have contained - resources, where the reference in the outer resource which - links to the contained resource sometimes did does not get - populated with the actual target resource instance. Thanks to - Neal Acharya for reporting! - - - hapi-fhir-cli upload-terminology command now has an argument - "-b FOO" that lets you add an authorization header in the form - Authorization: Bearer FOO]]> - - - Parser failed to successfully encode a custom resource - if it contained custom fields that also used custom - types. Thanks to GitHub user @sjanic for reporting! - - - Inprove handling of _text and _content searches in JPA server to do better - matching on partial strings - - - Servers in STU3 mode will now ignore any ID or VersionID found in the - resource body provided by the client when processing FHIR - update]]> operations. This change has been made - because the FHIR specification now requires servers to ignore - these values. Note that as a result of this change, resources passed - to @Update]]> methods will always have - null]]> ID - - - Add new methods to - AuthorizationInterceptor]]> - which allow user code to declare support for conditional - create, update, and delete. - - - When encoding a resource with a reference to another resource - that has a placeholder ID (e.g. urn:uuid:foo), the urn prefix - was incorrectly stripped from the reference. - - - Servers for STU3 (or newer) will no longer include a - Location:]]> header on responses for - read]]> operations. This header was - required in earlier versions of FHIR but has been removed - from the specification. - - - Fix NullPointerException when encoding an extension containing CodeableConcept - with log level set to TRACE. Thanks to Bill Denton for the report! - - - Add two new methods to the parser error handler that let users trap - invalid contained resources with no ID, as well as references to contained - resource that do not exist. - - - Improve performance when parsing resources containing contained resources - by eliminating a step where references were woven twice - - - Parser failed to parse resources containing an extension with a value type of - "id". Thanks to Raphael Mäder for reporting! - - - When committing a transaction in JPA server - where the transaction contained placeholder IDs - for references between bundles, the placeholder - IDs were not substituted with viewing - resources using the _history operation - - - HAPI root pom shouldn't include animal-sniffer plugin, - since that causes any projects which extend this to - be held to Java 6 compliance. - -
    - - - Performance has been improved for the initial FhirContext - object creation by avoiding a lot of unnecessary reflection. HAPI FHIR - 1.5 had a regression compared to previous releases - and this has been corrected, but other improvements have been - made so that this release is faster than previous releases too. -
    ]]> - In addition, a new "deferred scan" mode has been implemented for - even faster initialization on slower environments (e.g. Android). - See the performance documentation]]> - for more information. -
    ]]> - The following shows our benchmarks for context initialization across several - versions of HAPI: - -
  • Version 1.4: 560ms
  • -
  • Version 1.5: 800ms
  • -
  • Version 1.6: 340ms
  • -
  • Version 1.6 (deferred mode): 240ms
  • - - ]]> -
    - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Spring (JPA, Web Tester): 4.2.5 -> 4.3.0
  • -
  • Spring-Data (JPA): 1.9.2 -> 1.10.1
  • - -
  • Hibernate Search (JPA): 5.5.2 -> 5.5.3
  • -
  • Jetty (CLI): 9.3.9 -> 9.3.10
  • - - ]]> -
    - - Remove some clases that were deprecated over a year ago and have - suitable replacements: - -
  • QualifiedDateParam has been removed, but DateParam may be used instead
  • -
  • PathSpecification has been removedm but Include may be used instead
  • - - ]]> -
    - - ResponseValidatingInterceptor threw an InternalErrorException (HTTP 500) for operations - that do not return any content (e.g. delete). Thanks to Mohammad Jafari for reporting! - - - REST server now throws an HTTP 400 instead of an HTTP 500 if an operation which takes - a FHIR resource in the request body (e.g. create, update) contains invalid content that - the parser is unable to parse. Thanks to Jim Steel for the suggestion! - - - Deprecate fluent client search operations without an explicit declaration of the - bundle type being used. This also means that in a client - .search()]]> - operation, the - .returnBundle(Bundle.class)]]> - needs to be the last statement before - .execute()]]> - - - Server now respects the parameter _format=application/xml+fhir"]]> - which is technically invalid since the + should be escaped, but is likely to be used. Also, - a parameter of _format=html]]> can now be used, which - forces SyntaxHighlightingInterceptor to use HTML even - if the headers wouldn't otherwise trigger it. - Thanks to Jim Steel for reporting! - - - Improve performance when parsing large bundles by fixing a loop over all of the - entries inthe bundle to stitch together cross-references, which was happening once - per entry instead of once overall. Thanks to Erick on the HAPI FHIR Google Group for - noticing that this was an issue! - - - JSON parser no longer allows the resource ID to be specified in an element called "_id" - (the correct one is "id"). Previously _id was allowed because some early FHIR examples - used that form, but this was never actually valid so it is now being removed. - - - JPA server now allows "forced IDs" (ids containing non-numeric, client assigned IDs) - to use the same logical ID part on different resource types. E.g. A server may now have - both Patient/foo and Obervation/foo on the same server.
    ]]> - Note that existing databases will need to modify index "IDX_FORCEDID" as - it is no longer unique, and perform a reindexing pass. -
    - - When serializing/encoding custom types which replace exsting choice fields by - fixing the choice to a single type, the parser would forget that the - field was a choice and would use the wrong name (e.g. "abatement" instead of - "abatementDateType"). Thanks to Yaroslav Kovbas for reporting and - providing a unit test! - - - JPA server transactions sometimes created an incorrect resource reference - if a resource being saved contained references that had a display value but - not an actual reference. Thanks to David Hay for reporting! - - - When performing a REST Client create or update with - Prefer: return=representation]]> set, - if the server does not honour the Prefer header, the client - will automatically fetch the resource before returning. Thanks - to Ewout Kramer for the idea! - - - DSTU3 structures now have - setFoo(List)]]> - and - setGetFooFirstRep()]]> - methods, bringing them back to parity with the HAPI - DSTU2 structures. Thanks to Rahul Somasunderam and - Claude Nanjo for the suggestions! - - - JPA server has now been refactored to use the - new FluentPath search parameter definitions - for DSTU3 resources. - - - RequestValidatingInterceptor and ResponseValidatingInterceptor - both have new method setIgnoreValidatorExceptions]]> - which causes validator exceptions to be ignored, rather than causing - processing to be aborted. - - - LoggingInterceptor on server has a new parameter - ${requestBodyFhir}]]> which logs the entire request body. - - - JAX-RS server module now supports DSTU3 resources (previously it only supported DSTU2). Thanks - to Phillip Warner for implementing this, and providing a pull request! - - - Generated conformance statements for DSTU3 servers did not properly reference their - OperationDefinitions. Thanks - to Phillip Warner for implementing this, and providing a pull request! - - - Properly handle null arrays when parsing JSON resources. Thanks to Subhro for - fixing this and providing a pull request! - - - STU3 validator failed to validate codes where the - code was a child code within the code system that contained it - (i.e. not a top level code). Thanks to Jon - Zammit for reporting! - - - Restore the setType method in the DSTU1 Bundle - class, as it was accidentally commented out. Thanks - to GitHub user @Virdulys for the pull request! - - - JPA server now supports composite search parameters - where the type of the composite parameter is - a quantity (e.g. Observation:component-code-component-value-quantity) - - - Remove the Remittance resource from DSTU2 - structures, as it is not a real resource and - was causing issues with interoperability - with the .NET client. - - - CLI tool cache feature (-c) for upload-example task sometimes failed - to write cache file and exited with an exception. - - - Fix error message in web testing UI when loading pages in a search - result for STU3 endpoints. - - - When encoding JSON resource, the parser will now always - ensure that XHTML narrative content has an - XHTML namespace declaration on the first - DIV tag. This was preventing validation for - some resources using the official validator - rules. - - - Server failed to invoke operations when the name - was escaped (%24execute instead of $execute). - Thanks to Michael Lawley for reporting! - - - JPA server transactions containing a bundle that has multiple entries - trying to delete the same resource caused a 500 internal error - - - JPA module failed to index search parameters that mapped to a Timing datatype, - e.g. CarePlan:activitydate - - - Add a new option to the CLI run-server command called --lowmem]]>. - This option disables some features (e.g. fulltext search) in order to allow the - server to start in memory-constrained environments (e.g Raspberry Pi) - - - When updating a resource via an update operation on the server, if the ID of the - resource is not present in the resource body but is present on the URL, this will - now be treated as a warning instead of as a failure in order to be a bit more - tolerant of errors. If the ID is present in the body but does not agree with the - ID in the URL this remains an error. - - - Server / JPA server date range search params (e.g. Encounter:date) now treat - a single date with no comparator (or the eq comparator) as requiring that the - value be completely contained by the range specified. Thanks to Chris Moesel - for the suggestion. - - - In server, if a parameter was annotated with the annotation, the - count would not appear in the self/prev/next links and would not actually be applied - to the search results by the server. Thanks to Jim Steele for letting us know! - - - Conditional update on server failed to process if the conditional URL did not have any - search parameters that did not start with an underscore. E.g. "Patient?_id=1" failed - even though this is a valid conditional reference. - - - JPA server can now be configured to allow external references (i.e. references that - point to resources on other servers). See - JPA Documentation]]> for information on - how to use this. Thanks to Naminder Soorma for the suggestion! - - - When posting a resource to a server that contains an invalid value in a boolean field - (e.g. Patient with an active value of "1") the server should return an HTTP 400, not - an HTTP 500. Thanks to Jim Steel for reporting! - - - Enable parsers to parse and serialize custom resources that contain custom datatypes. - An example has been added which shows how to do this - here]]> - - - JSON parser was incorrectly encoding resource language attribute in JSON as an - array instead of a string. Thanks to David Hay for reporting! - - - Sébastien Rivière contributed an excellent pull request which adds a - number of enhancements to JAX-RS module: - -
  • Enable the conditional update and delete
  • -
  • Creation of a bundle provider, and support of the @Transaction
  • -
  • Bug fix on the exceptions handling as some exceptions throw outside bean context were not intercept.
  • -
  • Add the possibility to have the stacktrace in the jaxrsException
  • - - ]]> -
    - - FhirTerser.cloneInto method failed to clone correctly if the source - had any extensions. Thanks to GitHub user @Virdulys for submitting and - providing a test case! - - - Update DSTU2 InstanceValidator to latest version from upstream - - - Web Testing UI was not able to correctly post an STU3 transaction - - - DateTime parser incorrectly parsed times where more than 3 digits of - precision were provided on the seconds after the decimal point - - - Improve error messages when the $validate operation is called but no resource - is actually supplied to validate - - - DSTU2+ servers no longer return the Category header, as this has been - removed from the FHIR specification (and tags are now available in the - resource body so the header was duplication/wasted bandwidth) - - - Create and Update operations in server did not - include ETag or Last-Modified headers even though - the spec says they should. Thanks to Jim Steel for - reporting! - - - Update STU3 client and server to use the new sort parameter style (param1,-param2,param). Thanks to GitHub user @euz1e4r for - reporting! - - - QuantityClientParam#withUnit(String) put the unit into the system part of the - parameter value - - - Fluent client searches with date parameters were not correctly using - new prefix style (e.g. gt) instead of old one (e.g. >) - - - Some built-in v3 code systems for STU3 resources were missing - certain codes, which caused false failures when validating - resources. Thanks to GitHub user @Xoude for reporting! - - - Some methods on DSTU2 model structures have JavaDocs that - incorrectly claim that the method will not return null when - in fact it can. Thanks to Rick Riemer for reporting! - - - ResponseHighlightingInterceptor has been modified based on consensus - on Zulip with Grahame that requests that have a parameter of - _format=json]]> or - _format=xml]]> will output raw FHIR content - instead of HTML highlighting the content as they previously did. - HTML content can now be forced via the (previously existing) - _format=html]]> or via the two newly added - values - _format=html/json]]> and - _format=html/xml]]>. Because of this - change, the custom - _raw=true]]> mode has been deprecated and - will be removed at some point. - - - Operation definitions (e.g. for $everything operation) in the generated - server conformance statement should not include the $ prefix in the operation - name or code. Thanks to Dion McMurtrie for reporting! - - - Server generated OperationDefinition resources did not validate - due to some missing elements (kind, status, etc.). - Thanks to - Michael Lawley for reporting! - - - Operations that are defined on multiple resource provider types with - the same name (e.g. "$everything") are now automatically exposed by the server - as separate OperationDefinition resources per resource type. Thanks to - Michael Lawley for reporting! - - - OperationDefinition resources generated automatically by the server for operations - that are defined within resource/plain providers incorrectly stated that - the maximum cardinality was "*" for non-collection types with no explicit - maximum stated, which is not the behaviour that the JavaDoc on the - annotation describes. Thanks to Michael Lawley - for reporting! - - - Server parameters annotated with - @Since]]> - or - @Count]]> - which are of a FHIR type such as IntegerDt or DateTimeType will - now be set to null if the client's URL does not - contain this parameter. Previously they would be populated - with an empty instance of the FHIR type, which was inconsistent with - the way other server parameters worked. - - - Server now supports the _at parameter (including multiple repetitions) - for history operation - - - - AuthorizationInterceptor can now allow or deny requests to extended - operations (e.g. $everything) - - - DecimalType used BigDecimal constructor instead of valueOf method to - create a BigDecimal from a double, resulting in weird floating point - conversions. Thanks to Craig McClendon for reporting! - - - Remove the depdendency on a method from commons-lang3 3.3 which was - causing issues on some Android phones which come with an older version - of this library bundled. Thanks to Paolo Perliti for reporting! - - - Parser is now better able to handle encoding fields which have been - populated with a class that extends the expected class - - - When declaring a child with - order=Child.REPLACE_PARENT]]> - the serialized form still put the element at the - end of the resource instead of in the correct - order - - - Fix STU3 JPA resource providers to allow validate operation - at instance level - -
    - - - Security Fix: XML parser was vulnerable to XXE (XML External Entity) - processing, which could result in local files on disk being disclosed. - See this page]]> - for more information. - Thanks to Jim Steel for reporting! - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Hibernate (JPA, Web Tester): 5.0.7 -> 5.1.0
  • -
  • Spring (JPA, Web Tester): 4.2.4 -> 4.2.5
  • -
  • SLF4j (All): 1.7.14 -> 1.7.21
  • - - ]]> -
    - - Support comments when parsing and encoding both JSON and XML. Comments are retrieved - and added to the newly created methods - IBase#getFormatCommentsPre() and - IBase#getFormatCommentsPost() - - - Added options to the CLI upload-examples command which allow it to cache - the downloaded content file, or use an arbitrary one. Thanks to Adam Carbone - for the pull request! - - - REST search parameters with a prefix/comparator had not been updated to use - the DSTU2 style prefixes (gt2011-01-10) instead of the DSTU1 style prefixes - (>2011-01-01). The client has been updated so that it uses the new prefixes - if the client has a DSTU2+ context. The server has been updated so that it now - supports both styles. -
    ]]> - As a part of this change, a new enum called - ParamPrefixEnum]]> - has been introduced. This enum replaces the old - QuantityCompararatorEnum]]> - which has a typo in its name and can not represent several new prefixes added since - DSTU1. -
    - - JPA server number and quantity search params now follow the rules for the - use of precision in search terms outlined in the - search page]]> of the - FHIR specification. For example, previously a 1% tolerance was applied for - all searches (10% for approximate search). Now, a tolerance which respects the - precision of the search term is used (but still 10% for approximate search). - - - Fix a failure starting the REST server if a method returns an untyped List, which - among other things prevented resource provider added to the server - as CDI beans in a JBoss enviroment. Thanks to GitHub user fw060 (Fei) for - reporting and figuring out exactly why this wasn't working! - - - JPA server now supports :above and :below qualifiers on URI search params - - - Add optional support (disabled by default for now) to JPA server to support - inline references containing search URLs. These URLs will be resolved when - a resource is being created/updated and replaced with the single matching - resource. This is being used as a part of the May 2016 Connectathon for - a testing scenario. - - - The server no longer adds a - WWW-Authenticate]]> - header to the response if any resource provider code throws an - AuthenticationException]]>. This header is - used for interactive authentication, which isn't generally - appropriate for FHIR. We added code to add this header a long time - ago for testing purposes and it never got removed. Please let us - know if you need the ability to add this header automatically. Thanks - to Lars Kristian Roland for pointing this out. - - - In the client, the create/update operations on a Binary resource - (which use the raw binary's content type as opposed to the FHIR - content type) were not including any request headers (Content-Type, - User-Agent, etc.) Thanks to Peter Van Houte of Agfa Healthcare for - reporting! - - - Handling of Binary resources containing embedded FHIR resources for - create/update/etc operations has been corrected per the FHIR rules - outlined at - Binary Resource]]> - in both the client and server. -
    ]]> - Essentially, if the Binary contains something - that isn't FHIR (e.g. an image with an image content-type) the - client will send the raw data with the image content type to the server. The - server will place the content type and raw data into a Binary resource instance - and pass those to the resource provider. This part was already correct previous - to 1.5. -
    ]]> - On the other hand, if the Binary contains a FHIR content type, the Binary - is now sent by the client to the server as a Binary resource with a FHIR content-type, - and the embedded FHIR content is contained in the appropriate fields. The server - will pass this "outer" Binary resource to the resource provider code. -
    - - The RequestDetails and ActionRequestDetails objects which are passed to - server interceptor methods and may also be used as server provider method - arguments now has a new method - Map<String, String> getUserData() - ]]> - which can be used to pass data and objects between interceptor methods to - to providers. This can be useful, for instance, if an authorization - interceptor wants to pass the logged in user's details to other parts - of the server. - - - IServerInterceptor#incomingRequestPreHandled() is called - for a @Validate method, the resource was not populated in the - ActionRequestDetails argument. Thanks to Ravi Kuchi for reporting! - ]]> - - - [baseUrl]/metadata with an HTTP method - other than GET (e.g. POST, PUT) should result in an HTTP 405. Thanks to - Michael Lawley for reporting! - ]]> - - - Fix a server exception when trying to automatically add the profile tag - to a resource which already has one or more profiles set. Thanks to - Magnus Vinther for reporting! - - - QuantityParam parameters being used in the RESTful server were ignoring - the - :missing]]> - qualifier. Thanks to Alexander Takacs for reporting! - - - Annotation client failed with an exception if the response contained - extensions on fields in the resonse Bundle (e.g. Bundle.entry.search). - Thanks to GitHub user am202 for reporting! - - - Primitive elements with no value but an extension were sometimes not - encoded correctly in XML, and sometimes not parsed correctly in JSON. - Thanks to Bill de Beaubien for reporting! - - - The Web Testing UI has long had an issue where if you click on a button which - navigates to a new page (e.g. search, read, etc) and then click the back button - to return to the original page, the button you clicked remains disabled and can't - be clicked again (on Firefox and Safari). This is now fixed. Unfortunately the fix means that the - buttom will no longer show a "loading" spinner, but there doesn't seem to - be another way of fixing this. Thanks to Mark Scrimshire for reporting! - - - Extensions found while parsing an object that doesn't support extensions are now - reported using the IParserErrorHandler framework in the same way that - other similar errors are handled. This allows the parser to be more lenient - when needed. - - - Improve error message if incorrect type is placed in a list field in the data model. Java - uses generics to prevent this at compile time, but if someone is in an environment without - generics this helps improve the error message at runtime. Thanks to Hugo Soares for - suggesting. - - - Prevent an unneeded warning when parsing a resource containing - a declared extension. Thanks to Matt Blanchette for reporting! - - - Web Tester UI did not invoke VRead even if a version ID was specified. Thanks - to Poseidon for reporting! - - - Per discussion on the FHIR implementer chat, the JPA server no - longer includes _revinclude matches in the Bundle.total count, or the - page size limit. - - - JPA server now persists search results to the database in a new table where they - can be temporaily preserved. This makes the JPA server much more scalable, since it - no longer needs to store large lists of pages in memory between search invocations. -
    ]]> - Old searches are deleted after an hour by default, but this can be changed - via a setting in the DaoConfig. -
    - - JPA servers' resource version history mechanism - has been adjusted so that the history table - keeps a record of all versions including the - current version. This has the very helpful - side effect that history no longer needs to be - paged into memory as a complete set. Previously - history had a hard limit of only being able to - page the most recent 20000 entries. Now it has - no limit. - - - JPA server returned the wrong Bundle.type value (COLLECTION, should be SEARCHSET) - for $everything operation responses. Thanks to Sonali Somase for reporting! - - - REST and JPA server should reject update requests where the resource body does not - contain an ID, or contains an ID which does not match the URL. Previously these - were accepted (the URL ID was trusted) which is incorrect according to the - FHIR specification. Thanks to GitHub user ametke for reporting! -
    ]]> - As a part of this change, server error messages were also improved for - requests where the URL does not contain an ID but needs to (e.g. for - an update) or contains an ID but shouldn't (e.g. for a create) -
    - - When fields of type BoundCodeDt (e.g. Patient.gender) - are serialized and deserialized using Java's native - object serialization, the enum binder was not - serialized too. This meant that values for the - field in the deserialized object could not be - modified. Thanks to Thomas Andersen for reporting! - - - REST Server responded to HTTP OPTIONS requests with - any URI as being a request for the server's - Conformance statement. This is incorrect, as only - a request for OPTIONS [base url]]]> should be treated as such. Thanks to Michael Lawley for reporting! - - - REST annotation style client was not able to handle extended operations - ($foo) where the response from the server was a raw resource instead - of a Parameters resource. Thanks to Andrew Michael Martin for reporting! - - - JPA server applies _lastUpdated filter inline with other searches wherever possible - instead of applying this filter as a second query against the results of the - first query. This should improve performance when searching against large - datasets. - - - Parsers have new method - setDontEncodeElements]]> - which can be used to force the parser to not encode certain elements - in a resource when serializing. For example this can be used to omit - sensitive data or skip the resource metadata. - - - JPA server database design has been adjusted - so that different tables use different sequences - to generate their indexes, resulting in more sequential - resource IDs being assigned by the server - - - Server now correctly serves up Binary resources - using their native content type (instead of as a - FHIR resource) if the request contains an accept - header containing "application/xml" as some browsers - do. - - - DSTU2 resources now have a - getMeta()]]> method which returns a - modifiable view of the resource metadata for convenience. This - matches the equivalent method in the DSTU3 structures. - - - Add a new method to FhirContext called - setDefaultTypeForProfile - ]]> - which can be used to specify that when recources are received which declare - support for specific profiles, a specific custom structures should be used - instead of the default. For example, if you have created a custom Observation - class for a specific profile, you could use this method to cause your custom - type to be used by the parser for resources in a search bundle you receive. -
    - See the documentation page on - Profiles and Extensions - for more information. - ]]> -
    - - Parsing/Encoding a custom resource type which extends a - base type sometimes caused the FhirContext to treat all future - parses of the same resource as using the custom type even when - this was not wanted. -
    ]]> - Custom structures may now be explicitly declared by profile - using the - setDefaultTypeForProfile - ]]> - method. -
    ]]> - This issue was discovered and fixed as a part of the implementation of issue #315. -
    - - Set up the tinder plugin to work as an ant task - as well as a Maven plugin, and to use external - sources. Thanks to Bill Denton for the pull - request! - - - JPA server now allows searching by token - parameter using a system only and no code, - giving a search for any tokens which match - the given token with any code. Previously the - expected behaviour for this search - was not clear in the spec and HAPI had different - behaviour from the other reference servers. - - - Introduce a JAX-RS client provider which can be used instead of the - default Apache HTTP Client provider to provide low level HTTP - services to HAPI's REST client. See - JAX-RS & Alternate HTTP Client Providers]]> - for more information. -
    ]]> - This is useful in cases where you have other non-FHIR REST clients - using a JAX-RS provider and want to take advantage of the - rest of the framework. -
    ]]> - Thanks to Peter Van Houte from Agfa for the amazing work! -
    - - Parser failed with a NPE while encoding resources if the - resource contained a null extension. Thanks to - steve1medix for reporting! - - - In generated model classes (DSTU1/2) don't - use BoundCodeDt and BoundCodeableConceptDt for - coded fields which use example bindings. Thanks - to GitHub user Ricq for reporting! - - - @Operation will now infer the maximum number of repetitions - of their parameters by the type of the parameter. Previously if - a default max() value was not specified in the - @OperationParam annotation on a parameter, the maximum - was assumed to be 1. Now, if a max value is not explicitly specified - and the type of the parameter is a basic type (e.g. StringDt) the - max will be 1. If the parameter is a collection type (e.g. List<StringDt>) - the max will be * - ]]> - - - @Operation - may now use search parameter types, such as - TokenParam and - TokenAndListParam as values. Thanks to - Christian Ohr for reporting! - ]]> - - - Add databases indexes to JPA module search index tables - for the RES_ID column on each. This should help - performance when searching over large datasets. - Thanks to Emmanuel Duviviers for the suggestion! - - - DateTimeType should fail to parse 1974-12-25+10:00 as this is not - a valid time in FHIR. Thanks to Grahame Grieve for reporting! - - - When parsing a Bundle resource, if the Bundle.entry.request.url contains a UUID - but the resource body has no ID, the Resource.id will be populated with the ID from the - Bundle.entry.request.url. This is helpful when round tripping Bundles containing - UUIDs. - - - When parsing a DSTU3 bundle, references between resources did not have - the actual resource instance populated into the reference if the - IDs matched as they did in DSTU1/2. - - - Contained resource references on DSTU3 - resources were not serialized correctly when - using the Json Parser. Thanks to GitHub user - @fw060 for reporting and supplying a patch - which corrects the issue! - - - DSTU3 model classes equalsShallow and equalsDeep both did not work - correctly if a field was null in one object, but contained an empty - object in the other (e.g. a StringType with no actual value in it). These - two should be considered equal, since they would produce the exact same - wire format.
    ]]> - Thanks to GitHub user @ipropper for reporting and providing - a test case! -
    - - JPA server now supports searching for _tag:not=[tag]]]> - which enables finding resources that to not have a given tag/profile/security tag. - Thanks to Lars Kristian Roland for the suggestion! - - - Extensions containing resource references did not get encoded correctly - some of the time. Thanks to Poseidon for reporting! - - - Parsers (both XML and JSON) encoded the first few elements of DSTU3 structures in the wrong order: - Extensions were placed before any other content, which is incorrect (several - elements come first: meta, text, etc.) - - - In server implementations, the Bundle.entry.fullUrl was not getting correctly - populated on Hl7OrgDstu2 servers. Thanks to Christian Ohr for reporting! - - - Ensure that element IDs within resources (i.e. IDs on elements other than the - resource itself) get serialized and parsed correctly. Previously, these didn't get - serialized in a bunch of circumstances. Thanks to Vadim Peretokin for reporting - and providing test cases! - - - Improve CLI error message if the tool can't bind to the requested port. Thanks - to Claude Nanjo for the suggestion! - - - Server param of _summary=text]]> did not - include mandatory elements in return as well as - the text element, even though the FHIR specification - required it. - - - Remove invalid resource type "Documentation" from DSTU2 - structures. - - - JPA server did not respect target types for search parameters. E.g. Appointment:patient has - a path of "Appointment.participant.actor" and a target type of "Patient". The search path - was being correctly handled, but the target type was being ignored. - - - RestfulServer now manually parses URL parameters instead of relying on the container's - parsed parameters. This is useful because many Java servlet containers (e.g. Tomcat, Glassfish) - default to ISO-8859-1 encoding for URLs insetad of the UTF-8 encoding specified by - FHIR. - - - ResponseHighlightingInterceptor now doesn't highlight if the request - has an Origin header, since this probably denotes an AJAX request. - -
    - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Hibernate (JPA, Web Tester): 5.0.3 -> 5.0.7
  • -
  • Springframework (JPA, Web Tester): 4.2.2 -> 4.2.4
  • -
  • Phloc-Commons (Schematron Validator): 4.3.6 -> 4.4.4
  • -
  • Apache httpclient (Client): 4.4 -> 4.5.1
  • -
  • Apache httpcore (Client): 4.4 -> 4.4.4
  • -
  • SLF4j (All): 1.7.13 -> 1.7.14
  • - - ]]> -
    - - Remove a dependency on a Java 1.7 class - (ReflectiveOperationException) in several spots in the - codebase. This dependency was accidentally introduced in - 1.3, and animal-sniffer-plugin failed to detect it (sigh). - - - Add two new server interceptors: - RequestValidatingInterceptor - and - ResponseValidatingInterceptor - ]]> - which can be used to validate incoming requests or outgoing responses using the standard FHIR validation - tools. See the - Server Validation Page - ]]> - for examples of how to use these interceptors. These intereptors have both - been enabled on the - public test page. - ]]> - - - Make IBoundCodeableConcept and IValueSetEnumBinder serializable, - fixing an issue when trying to serialize model classes containing - bound codes. Thanks to Nick Peterson for the Pull Request! - - - Introduce a JAX-RS version of the REST server, which can be used - to deploy the same resource provider implementations which work - on the existing REST server into a JAX-RS (e.g. Jersey) environment. - Thanks to Peter Van Houte from Agfa for the amazing work! - - - CLI now supports writing to file:// URL for 'upload-examples' command - - - GZipped content is now supported for client-to-server uploads (create, update, transaction, etc.). - The server will not automatically detect compressed incoming content and decompress it (this can be - disabled using a RestfulServer configuration setting). A new client interceptor has been added - which compresses outgoing content from the client. - - - JPA server transaction attempted to validate resources twice each, - with one of these times being before anything had been committed to the - database. This meant that if a transaction contained both a Questionnaire - and a QuestionnaireResponse, it would fail because the QuestionnaireResponse - validator wouldn't be able to find the questionnaire. This is now corrected. - - - Add a new method to the generic/fluent client for searching: - .count(int)
    ]]> - This replaces the existing ".limitTo(int)" method which has - now been deprocated because it was badly named and undocumented. -
    - - Profile validator has been configured to allow extensions even if they - aren't explicitly declared in the profile. - - - Add a constraint that the Maven build will only run in JDK 8+. HAPI - remains committed to supporting JDK 6+ in the compiled library, but these - days it can only be built using JDK 8. Thanks to joelsch for the PR! - - - When serializing a value[x] field, if the value type was a profiled type (e.g. markdown is a - profile of string) HAPI 1.3 would use the base type in the element name, e.g. - valueString instead of valueMarkdown. After discussion with Grahame, this appears to - be incorrect behaviour so it has been fixed. - - - Support target parameter type in _include / _revinclude values, e.g. - _include=Patient:careProvider:Organization. Thanks to Joe Portner - for reporting! - - - Use ResponseHighlighterInterceptor in the hapi-fhir-jpaserver-example - project to provide nice syntax highlighting. Thanks to Rob Hausam for - noting that this wasn't there. - - - Introduce custom @CoverageIgnore annotation to hapi-fhir-base in order to - remove dependency on cobertura during build and in runtime. - - - Server-generated conformance statements incorrectly used /Profile/ instead - of /StructureDefinition/ in URL links to structures. - - - JsonParser has been changed so that when serializing numbers it will use - plain format (0.001) instead of scientific format (1e-3). The latter is - valid JSON, and the parser will still correctly parse either format (all - clients should be prepared to) but this change makes serialized - resources appear more consistent between XML and JSON. As a result of this - change, trailing zeros will now be preserved when serializing as well. - - - Add DSTU3 example to hapi-fhir-jpaserver-example. Thanks to Karl - Davis for the Pull Request! - - - RestfulServer#setUseBrowserFriendlyContentTypes has been deprecated and its - functionality removed. The intention of this feature was that if it - detected a request coming in from a browser, it would serve up JSON/XML - using content types that caused the browsers to pretty print. But - each browser has different rules for when to pretty print, and - after we wrote that feature both Chrome and FF changed their rules to break it anyhow. - ResponseHighlightingInterceptor provides a better implementation of - this functionality and should be used instead. - - - Narrative generator framework has removed the - ability to generate resource titles. This - functionality was only useful for DSTU1 - implementations and wasn't compatible - with coming changes to that API. - - - Remove dependency on Servlet-API 3.0+ by using methods available in 2.5 where possible. - Note that we continue to use Servlet-API 3.0+ features in some parts of the JPA API, so - running in an old serlvet container should be tested well before use. Thanks to Bill Denton - for reporting! - - - Add new methods to RestfulClientFactory allowing you to configure the size of the - client pool used by Apache HttpClient. Thanks to Matt Blanchette for the pull - request! - - - Add support for new modifier types on Token search params in Server and - annotation client. - - - Server conformance statement should include search parameter chains if the - chains are explicitly defined via @Search(whitelist={....}). Thanks to lcamilo15 - for reporting! - - - Remove afterPropertiesSet() call in Java config for JPA - server's EntityManagerFactory. This doesn't need to be called - manually, the the manual call led to a warning about - the EntityManager being created twice. - - - Allow server to correctly figure out it's own address even if the container provides - a Servlet Context Path which does not include the root. Thanks to Petro Mykhaylyshyn - for the pull request! - -
    - - - Bump the version of a few dependencies to the - latest versions (dependent HAPI modules listed in brackets): - -
  • Commons-lang3 (Core): 3.3.2 -> 3.4
  • -
  • Logback (Core): 1.1.2 -> 1.1.3
  • -
  • SLF4j (Core): 1.7.102 -> 1.7.12
  • -
  • Springframework (JPA, Web Tester): 4.1.5 -> 4.2.2
  • -
  • Hibernate (JPA, Web Tester): 4.2.17 -> 5."
  • -
  • Hibernate Validator (JPA, Web Tester): 5.2.1 -> 5.2.2
  • -
  • Derby (JPA, CLI, Public Server): 10.11.1.1 -> 10.12.1.1
  • -
  • Jetty (JPA, CLI, Public Server): 9.2.6.v20141205 -> 9.3.4.v20151007
  • - - ]]> -
    - - JPA and Tester Overlay now use Spring Java config files instead - of the older XML config files. All example projects have been updated. - - - JPA server removes duplicate resource index entries before storing them - (e.g. if a patient has the same name twice, only one index entry is created - for that name) - - - JPA server did not correctly index search parameters of type "reference" where the - path had multiple entries (i.e. "Resource.path1 | Resource.path2") - - - JPA server _history operations (server, type, instance) not correctly set the - Bundle.entry.request.method to POST or PUT for create and updates of the resource. - - - Support AND/OR on _id search parameter in JPA - - - Constructor for DateRanfeParam which dates in two DateParam instances was ignoring - comparators on the DateParam. - - - In JSON parsing, finding an object where an array was expected led to an unhelpful - error message. Thanks to Avinash Shanbhag for reporting! - - - JPA server gave an unhelpful error message if $meta-add or $meta-delete were called - with no meta elements in the input Parameters - - - Narrative generator did not include OperationOutcome.issue.diagnostics in the - generated narrative. - - - Clients (generic and annotation) did not populate the Accept header on outgoing - requests. This is now populated to indicate that the client supports both XML and - JSON unless the user has explicitly requested one or the other (in which case the - appropriate type only will be send in the accept header). Thanks to - Avinash Shanbhag for reporting! - - - QuestionnaireResponse validator now allows responses to questions of - type OPENCHOICE to be of type 'string' - - - JPA server should reject resources with a reference that points to an incorrectly typed - resource (e.g. points to Patient/123 but resource 123 is actually an Observation) or points - to a resource that is not valid in the location it is found in (e.g. points to Patient/123 but - the field supposed to reference an Organization). Thanks to Bill de Beaubien for reporting! - - - In server, if a client request is received and it has an Accept header indicating - that it supports both XML and JSON with equal weight, the server's default is used instead of the first entry in the list. - - - JPA server now supports searching with sort by token, quantity, - number, Uri, and _lastUpdated (previously only string, date, and _id - were supported) - - - Fix issue in JPA where a search with a _lastUpdated filter which matches no results - would crash if the search also had a _sort - - - Fix several cases where invalid requests would cause an HTTP 500 instead of - a more appropriate 400/404 in the JPA server (vread on invalid version, - delete with no ID, etc.) - - - Fix narrative generation for DSTU2 Medication resource - - - Profile validator now works for valuesets which use - v2 tables - - - JPA server Patient/[id]/$everything operation now supports - _lastUpdated filtering and _sort'ing of results. - - - Fix parser issue where profiled choice element datatypes (e.g. value[x] where one allowable - type is Duration, which is a profile of Quantity) get incorrectly encoded using the - profiled datatype name instead of the base datatype name as required by the FHIR - spec. Thanks to Nehashri Puttu Lokesh for reporting! - - - Some generated Enum types in DSTU2 HAPI structures - did not have latest valueset definitions applied. Thanks - to Bill de Beaubien for reporting! - - - JPA server can now successfully search for tokens pointing at code values - (values with no explicit system but an implied one, such as Patient.gender) - even if the system is supplied in the query. - - - Correct issues with Android library. Thanks to - Thomas Andersen for the submission! - - - JPA server incorrectly rejected match URLs - if they did not contain a question mark. Thanks - to Bill de Beaubien for reporting! - - - Remove invalid entries in OSGi Manifest. Thanks - to Alexander Kley for the fix! - - - JPA server now supports $everything on Patient and Encounter types (patient and encounter instance was already supported) - - - Generic client operation invocations now - have an additional inline method for generating the input - Parameters using chained method calls instead - of by passing a Parameters resource in - - - Parsing an XML resource where the XHTML - namespace was declared before the beginning - of the narrative section caused an invalid - re-encoding when encoding to JSON. - - - Conditional deletes in JPA did not correctly - process if the condition had a chain or a - qualifier, e.g. "Patient?organization.name" or - "Patient.identifier:missing" - - - Generic/fluent client search can now be - performed using a complete URL supplied - by user code. Thanks to Simone Heckmann - pointing out that this was needed! - - - Refactor JPA $everything operations so that - they perform better - - - Server operation methods can now declare the - ID optional, via - @IdParam(optional=true) - meaning that the same operation can also be invoked - at the type level. - - - Make JPA search queries with _lastUpdated parameter a bit more efficient - - - Clean up Android project to make it more lightweight and remove a - number of unneeded dependencies. Thanks to Thomas Andersen - for the pull request! - - - Fix a crash when encoding a Binary resource in JSON encoding - if the resource has no content-type - - - JPA server now supports read/history/search in transaction entries - by calling the actual implementing method in the server (previously - the call was simulated, which meant that many features did not work) - - - ResourceReferenceDt#loadResource(IRestfulClient) did not - use the client's read functionality, so it did not - handle JSON responses or use interceptors. Thanks to - JT for reporting! - - - JPA server maximumn length for a URI search parameter has been reduced from - 256 to 255 in order to accomodate MySQL's indexing requirements - - - Server failed to respond correctly to compartment search operations - if the same provider also contained a read operation. Thanks to GitHub user - @am202 for reporting! - - - Fix issue in testpage-overlay's new Java configuration where only the first - configured server actually gets used. - - - Introduce - IJpaServerInterceptor]]> - interceptors for JPA server which can be used for more fine grained operations. - - - Parser (XML and JSON) shouldn't encode an ID tag in resources - which are part of a bundle when the resource has a UUID/OID - ID. - - - Add ability for a server REST resource provider @Search method - to declare that it should allow even parameters it doesn't - understand. - - - Correctly set the Bundle.type value on all pages of a search result in - the server, and correcltly set the same value in JPA server $everything - results. - - - JPA $everything operations now support new parameters _content - and _text, which work the same way as the same parameters on a - search. This is experimental, since it is not a part of the core - FHIR specification. - - - Process "Accept: text/xml" and "Accept: text/json" headers was - wanting the equivalent FHIR encoding styles. These are not - correct, but the intention is clear so we will honour them - just to be helpful. - - - Generated Enum types for some ValueSets did not include all - codes (specifically, ValueSets which defined concepts containing - child concepts did not result in Enum values for the child concepts) - - - In the JPA server, order of transaction processing should be - DELETE, POST, PUT, GET, and the order should not matter - within entries with the same verb. Thanks to Bill de Beaubien - for reporting! - - - Add the ability to wire JPA conformance providers - using Spring (basically, add default constructors - and setters to the conformance providers). Thanks - to C. Mike Bylund for the pull request! - -
    - - - JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire - if one is declared. - - - SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions. - - - Model classes do not use BoundCodeableConcept for example bindings that do not - actually point to any codes (e.g. Observation.interpretation). Thanks - to GitHub user @steve1medix for reporting! - - - Server now exports operations as separate resources instead of as contained resources - within Conformance - - - Add new operation $get-resource-counts which will replace the resource - count extensions exported in the Conformance statement by the JPA - server. - - - JPA server sorting often returned unexpected orders when multiple - indexes of the same type were found on the same resource (e.g. multiple string indexed fields). Thanks to Travis Cummings for reporting! - - - Add another method to IServerInterceptor which converts an exception generated on the server - into a BaseServerResponseException. This is useful so that servers using ResponseHighlighterInterceptor - will highlight exceptions even if they aren't created with an OperationOutcome. - - - XmlParser and JsonParser in DSTU2 mode should not encode empty - tags in resource. Thanks to Bill De Beaubien for reporting! - - - OperationDefinitions generated by server did not properly document - their return parameters or the type of their input parameters. - - - Operations in server generated conformance statement should only - appear once per name, since the name needs to be unique. - - - Resources and datatypes are now serializable. This is an - experimental feature which hasn't yet been extensively tested. Please test and give us your feedback! - - - Switch REST server to using HttpServletRequest#getContextPath() to get - the servlet's context path. This means that the server should behave more - predictably, and should work in servlet 2.4 environments. Thanks to - Ken Zeisset for the suggestion! - - - Vagrant environment now has an apt recipt to ensure that - package lists are up to date. Thanks to GitHub user - Brian S. Corbin (@corbinbs) for thr contribution! - - - JPA server and generic client now both support the _tag search parameter - - - Add support for BATCH mode to JPA server transaction operation - - - Server was not correctly unescaping URL parameter values with - a trailing comma or an escaped backslash. Thanks to GitHub user - @SherryH for all of her help in diagnosing this issue! - - - Avoid crash when parsing if an invalid child element is found in - a resource reference. - - - Create new android specialty libraries for DSTU1 and DSTU2 - - - Throwing a server exception (e.g. AuthenticationException) in a server interceptor's - incomingRequestPreProcessed method resulted in the server returning an HTTP 500 instead - of the appropriate error code for the exception being thrown. Thanks to Nagesh Bashyam - for reporting! - - - Fix issue in JSON parser where invalid contained resources (missing - a resourceType element) fail to parse with a confusing NullPointerException. - Thanks to GitHub user @hugosoares for reporting! - - - JPA server now implements the $validate-code operation - - - HAPI-FHIR now has support for _summary and _elements parameters, in server, client, - and JPA server. - - - _revinclude results from JPA server should have a Bundle.entry.search.mode of - "include" and not "match". Thanks to Josh Mandel for reporting! - - - Resource references using resource instance objects instead of resource IDs - will correctly qualify the IDs with the resource type if they aren't already qualified - - - Testpage Overlay project now properly allows a custom client - factory to be used (e.g. for custom authentication, etc.) Thanks - to Chin Huang (@pukkaone) for the pull request! - - - JPA server should reject IDs containing invalid characters (e.g. "abc:123") - but should allow client assigned IDs that contain text but do not start with - text. Thanks to Josh Mandel for reporting! - - - :text modifier on server and JPA server did not work correctly. Thanks to - Josh Mandel for reporting! - - - Fix issue in client where parameter values containing a comma were - sometimes double escaped. - - - _include parameters now support the new _include:recurse=FOO]]> - syntax that has been introduced in DSTU2 in the Client, Server, and JPA Server modules. - Non-recursive behaviour is now the default (previously it was recursive) and :recurse - needs to be explicitly stated in order to support recursion. - - - New operations added to JPA server to force re-indexing of all - resources (really only useful after indexes change or bugs are - fixed) - - - JPA server did not correctly index search parameters - of type "URI". Thanks to David Hay for reporting! Note that if you are using the JPA server, this change means that - there are two new tables added to the database schema. Updating existing resources in the database may fail unless you - set default values for the resource - table by issuing a SQL command similar to the following (false may be 0 or something else, depending on the database platform in use) - update hfj_resource set sp_coords_present = false;
    - update hfj_resource set sp_uri_present = false;
    ]]> -
    - - FIx issue in JPA server where profile declarations, tags, and - security labels were not always properly removed by an update that - was trying to remove them. Also don't store duplicates. - - - Instance $meta operations on JPA server did not previously return the - resource version and lastUpdated time - - - Server responses populate Bundle.entry.fullUrl if possible. Thanks - to Bill de Beaubien for reporting! - - - XML parser failed to initialize in environments where a very old Woodstox - library is in use (earlier than 4.0). Thanks to Bill de Beaubien for - reporting! - - - Invalid/unexpected attributes found when parsing composite elements - should be logged or reported to the parser error handler - - - JPA server can now store Conformance resources, per a request - from David Hay - - - ResponseHighlightingInterceptor now skips handling responses if it - finds a URL parameter of _raw=true]]> (in other - words, if this parameter is found, the response won't be returned as - HTML even if the request is detected as coming from a browser. - - - RestfulServer now supports dynamically adding and removing resource providers - at runtime. Thanks to Bill Denton for adding this. - - - JPA server now correctly suppresses contents of deleted resources - in history - - - JPA server returned deleted resources in search results when using the _tag, _id, _profile, or _security search parameters - - - Fix issue with build on Windows. Thanks to Bryce van Dyk for the pull request! - - - JPA server now supports $validate operation completely, including delete mode - and profile validation using the RI InstanceValidator - -
    - - - Add support for reference implementation structures. - - - Parsers did not encode the resource meta element if the resource - had tags but no other meta elements. Thanks to Bill de Beaubien and - Claude Nanjo for finding this. - - - Correct performance issue with :missing=true search requests where the parameter is a resource link. Thanks to wanghaisheng for all his help in testing this. - - - The self link in the Bundle returned by searches on the server does not respect the - server's address strategy (which resulted in an internal IP being shown on fhirtest.uhn.ca) - - - Introduce ResponseHighlighterInterceptor, which provides syntax highlighting on RESTful server responses - if the server detects that the request is coming from a browser. This interceptor has been added - to fhirtest.uhn.ca responses. - - - Performing a create operation in a client used an incorrect URL if the - resource had an ID set. ID should be ignored for creates. Thanks to - Peter Girard for reporting! - - - Add better addXXX() methods to structures, which take the datatype being added as a parameter. Thanks to Claude Nanjo for the - suggestion! - - - Add a new parser validation mechanism (see the - validation page]]> for info) which can be - used to validate resources as they are being parsed, and optionally fail if invalid/unexpected - elements are found in resource bodies during parsing. - - - IParser#parseResource(Class, String) method, which is used to parse a resource into the given - structure will now throw a DataFormatException if the structure is for the wrong type of - resource for the one actually found in the input String (or Reader). For example, if a Patient - resource is being parsed into Organization.class this will now cause an error. Previously, - the XML parser would ignore the type and the JSON parser would fail. This also caused - operations to not parse correctly if they returned a resource type other than - parameters with JSON encoding (e.g. the $everything operation on UHN's test server). - Thanks to Avinash Shanbhag for reporting! - - - Web tester UI now supports _revinclude - - - Support link elements in Bundle.entry when parsing in DSTU2 mode - using the old (non-resource) Bundle class. Thanks to GitHub user - @joedai for reporting! - - - LoggingInterceptor for server now supports logging DSTU2 extended operations by name - - - Woodstox XML parser has a default setting to limit the maximum - length of an attribute to 512kb. This caused issues handling - large attachments, so this setting has been increased to 100Mb. - Thanks to Nikos Kyriakoulakos for reporting! - - - Some HTML entities were not correctly converted during parsing. Thanks to - Nick Kitto for reporting! - - - In the JPA Server: - Transactions creating resources with temporary/placeholder resource IDs - and other resources with references to those placeholder IDs previously - did not work if the reference did not contain the resource type - (e.g. Patient/urn:oid:0.1.2.3 instead of urn:oid:0.1.2.3). The - latter is actually the correct way of specifying a reference to a - placeholder, but the former was the only way that worked. Both forms - now work, in order to be lenient. Thanks to Bill De Beaubien for - reporting! - - - When parsing Bundles, if Bundle.entry.base is set to "cid:" (for DSTU1) - or "urn:uuid:" / "urn:oid:" (for DSTU2) this is now correctly passed as - the base in resource.getId(). Conversely, when - encoding bundles, if a resource ID has a base defined, - and Bundle.entry.base is empty, it will now be - automatically set by the parser. - - - Add fluent client method for validate operation, and support the - new DSTU2 style extended operation for $validate if the client is - in DSTU2 mode. Thanks to Eric from the FHIR Skype Implementers chat for - reporting. - - - Server now supports complete Accept header content negotiation, including - q values specifying order of preference. Previously the q value was ignored. - - - Server in DSTU2 mode now indicates that whether it has support for Transaction operation or not. Thanks to Kevin Paschke for pointing out that this wasn't working! - - - Questionnaire.title now gets correctly indexed in JPA server (it has no path, so it is a special case) - - - JPA server now supports ifNoneMatch in GET within a transaction request. - - - DateRangeParam now supports null values in the constructor for lower or upper bounds (but - still not both) - - - Generic/fluent client and JPA server now both support _lastUpdated search parameter - which was added in DSTU2 - - - JPA server now supports sorting on reference parameters. Thanks to - Vishal Kachroo for reporting that this wasn't working! - - - Prevent Last-Updated header in responses coming back to the client from - overwriting the 'lastUpdated' value in the meta element in DSTU2 - resources. This is important because 'lastUpdated' can have more - precision than the equivalent header, but the client previously - gave the header priority. - - - JPA server supports _count parameter in transaction containing search URL (nested search) - - - DSTU2 servers now indicate support for conditional create/update/delete in their - conformance statement. - - - Support for the Prefer header has been added to the server, client, and - JPA modules. - - - JPA server failed to search for deep chained parameters across multiple references, - e.g. "Location.partof.partof.organization". Thanks to Ismael Sarmento Jr for - reporting! - - - Prevent crash when encoding resources with contained resources - if the contained resources contained a circular reference to each other - - - Add $meta, $meta-add, and $meta-delete operations to generic client - - - - - Bump the version of a few dependencies to the - latest versions: - -
  • Phloc-commons (for schematron validation) 4.3.5 -> 4.3.6
  • -
  • Apache HttpClient 4.3.6 -> 4.4
  • -
  • Woodstox 4.4.0 -> 4.4.1
  • -
  • SLF4j 1.7.9 -> 1.7.10
  • -
  • Spring (used in hapi-fhir-jpaserver-base module) 4.1.3.RELEASE -> 4.1.5.RELEASE
  • - - ]]> -
    - - Add support for "profile" and "tag" elements in the resource Meta block - when parsing DSTU2 structures. - - - When a user manually creates the list of contained resources in a resource, - the encoder fails to encode any resources that don't have a '#' at the - start of their ID. This is unintuitive, so we now assume that '123' means '#123'. - Thanks to myungchoi for reporting and providing a test case! - - - Add methods for setting the default encoding (XML/JSON) and - oretty print behaviour in the Fluent Client. Thanks to Stackoverflow - user ewall for the idea. - - - JPA Server did not mark a resource as "no longer deleted" if it - was updated after being deleted. Thanks to Elliott Lavy and Lloyd - McKenzie for reporting! - - - Fix regression in 0.9 - Server responds with an HTTP 500 and a NullPointerException instead of an HTTP 400 and a useful error message if the client requests an unknown resource type - - - Add support for - _revinclude]]> - parameter in client, server, and JPA. - - - Include constants on resources (such as - Observation.INCLUDE_VALUE_STRING]]>) - have been switched in the DSTU2 structures to use - the new syntax required in DSTU2: [resource name]:[search param NAME] - insead of the DSTU1 style [resource name].[search param PATH] - - - When encoding resources, the parser will now convert any resource - references to versionless references automatically (i.e. it will - omit the version part automatically if one is present in the reference) - since references between resources must be versionless. Additionally, - references in server responses will omit the server base URL part of the - reference if the base matches the base for the server giving - the response. - - - Narrative generator incorrectly sets the Resource.text.status to 'generated' even if the - given resource type does not have a template (and therefore no narrative is actually generated). - Thanks to Bill de Beaubien for reporting! - - - Searching in JPA server with no search parameter returns deleted resources when it should exclude them. - - - Remove Eclipse and IntelliJ artifacts (.project, *.iml, etc) from version control. Thanks - to Doug Martin for the suggestion! - - - REST server methods may now have a parameter of - type NarrativeModeEnum which will be populated with - the value of the _narrative URL parameter - if one was supplied. Annotation client methods - may also include a parameter of this type, and it - will be used to populate this parameter on the request - URL if it is not null. Thanks to Neal Acharya for the - idea! - - - Android JAR now includes servlet-API classes, as the project will not - work without them. Thanks - - - Requested _include values are preserved across paging links when the - server returns multiple pages. Thanks to Bill de Beaubien for - reporting! - - - Add new server address strategy "ApacheProxyAddressStrategy" which uses - headers "x-forwarded-host" and "x-forwarded-proto" to determine the - server's address. This is useful if you are deploying a HAPI FHIR - server behind an Apache proxy (e.g. for load balancing or other reasons). - Thanks to Bill de Beaubien for contributing! - - - Resource references between separate resources found in a single - bundle did not get populated with the actual resource when parsing a - DSTU2 style bundle. Thanks to Nick Peterson for reporting and figuring - out why none of our unit tests were actually catching the problem! - - - JSON encoder did not encode contained resources when encoding - a DSTU2 style bundle. Thanks to Mohammad Jafari and baopingle - for all of their help in tracking this issue down and developing - useful unit tests to demonstrate it. - - - Client now supports invoking transcation using a DSTU2-style - Bundle resource as the input. - - - JPA Server $everything operation could sometimes include a duplicate copy of - the main focus resource if it was referred to in a deep chain. Thanks - to David Hay for reporting! - - - JPA Server $everything operation now allows a _count parameter - - - JPA server failed to index resources containing ContactPointDt elements with - populated values (e.g. Patient.telecom). Thanks to Mohammad Jafari for reporting! - - - Add a new configuration method on the parsers, - setStripVersionsFromReferences(boolean)]]> which - configures the parser to preserve versions in resource reference links when - encoding. By default, these are removed. - - - Terser's IModelVisitor now supplies to the path to the element. This is - an API change, but I don't think there are many users of the IModelVisitor yet. - Please let us know if this is a big hardship and we can find an alternate way - of making this change. - - - Prevent server from returning a Content-Location header for search - response when using the DSTU2 bundle format - - - JPA server (uhnfhirtest.uhn.ca) sometimes included an empty - "text" element in Bundles being returned. - - - Add a framework for the Web Tester UI to allow its internal FHIR client to - be configured (e.g. to add an authorization interceptor so that it adds - credentials to client requests it makes). Thanks to Harsha Kumara for - the suggestion! - - - Fix regression in early 1.0 builds where resource type sometimes does not get - populated in a resource ID when the resource is parsed. Thanks to - Nick Peterson for reporting, and for providing a test case! - - - Allow fluent/generic client users to execute a transaction using a raw string (containing a bundle resource) - as input instead of a Bundle resource class instance. - - - Disable date validation in the web tester UI, so that it is possible to - enter partial dates, or dates without times, or even test out invalid date - options. - - - Make BaseElement#getUndeclaredExtensions() and BaseElement#getUndeclaredExtensions() return - a mutable list so that it is possible to delete extensions from a resource instance. - - - Server conformance statement check in clients (this is the check - where the first time a given FhirContext is used to access a given server - base URL, it will first check the server's Conformance statement to ensure - that it supports the correct version of FHIR) now uses any - registered client interceptors. In addition, IGenericClient now has a method - "forceConformanceCheck()" which manually triggers this check. Thanks to - Doug Martin for reporting and suggesting! - - - Rename the Spring Bean definition for the JPA server EntityManager from - "myEntityManagerFactory" to just "entityManagerFactory" as this is the - default bean name expected in other parts of the Spring framework. - Thanks to Mohammad Jafari for the suggestion! - - - Improve error message when a user tries to perform a create/update with an invalid - or missing Content-Type header. Thanks to wanghaisheng for reporting! (This was - actually a three part bug, so the following two fixes also reference this - bug number) - - - Add support for :missing qualifier in generic/fluent client. - - - Add support for :missing qualifier in JPA server. - - - Add a new configuration method on the parsers, - setStripVersionsFromReferences(boolean)]]> which - configures the parser to preserve versions in resource reference links when - encoding. By default, these are removed. - - - Add an exception for RESTful clients/servers to represent the - HTTP 403 Forbidden status code. Thanks to Joel Costigliola for - the patch! - - - Transaction server operations incorrectly used the "Accept" header instead of the "Content-Type" header to determine the - POST request encoding. Thanks to Rene Spronk for providing a test case! - -
    - - - Support for DSTU2 features introduced: New resource definitions, Bundle resource, - encoding changes (ID in resource bodt, meta tag) - - - Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object - would be an array even if the field it was extending was not repeatable. This is not correct - according to the specification, nor can HAPI's parser parse this correctly. The encoder - has been corrected, and the parser has been adjusted to be able to handle resources with - extensions encoded in this way. Thanks to Mohammad Jafari for reporting! - - - Library now checks if custom resource types can be instantiated on startup - (e.g. because they don't have a no-argument constructor) in order to - avoid failing later - - - Bump a few dependency JARs to the latest versions in Maven POM: - -
  • SLF4j (in base module) - Bumped to 1.7.9
  • -
  • Apache HTTPClient (in base module) - Bumped to 4.3.6
  • -
  • Hibernate (in JPA module) - Bumped to 4.3.7
  • - - ]]> -
    - - IdDt failed to recognize local identifiers containing fragments that look like - real identifiers as being local identifiers even though they started with '#'. - For example, a local resource reference of "#aa/_history/aa" would be incorrectly - parsed as a non-local reference. - Thanks to Mohammad Jafari for reporting! - - - Last-Modified]]> - header in server was incorrectly using FHIR date format instead - of RFC-1123 format. - - - Server create and update methods failed with an IllegalArgumentException if - the method type was a custom resource definition type (instead of a built-in - HAPI type). Thanks to Neal Acharya for the analysis. - - - JPA server module now supports - _include]]> - value of - *]]>. Thanks to Bill de Beaubien for reporting! - - - IdDt method - - returned String (unlike all of the other "withFoo" methods on that class), - and did not work correctly if the IdDt already had a server base. This - has been corrected. Note that the return type for this method has been - changed, so code may need to be updated. - - - In previous versions of HAPI, the XML parser encoded multiple contained - resources in a single - <contained></contained>]]> - tag, even though the FHIR specification rerquires a separate - <contained></contained>]]> - tag for each resource. This has been corrected. Note that the parser will - correctly parse either form (this has always been the case) so this - change should not cause any breakage in HAPI based trading partners, but - may cause issues if other applications have been coded to depend on the - incorrect behaviour. Thanks to Mochaholic for reporting! - - - Custom/user defined resource definitions which contained more than one - child with no order defined failed to initialize properly. Thanks to - Andy Huang for reporting and figuring out where the - problem was! - - - RESTful Client now queries the server (only once per server base URL) to ensure that - the given server corresponds to the correct version of the FHIR specification, as - defined by the FhirContext. This behaviour can be disabled by setting the - appropriate configuration on the - RestfulClientConfig. Thanks to Grahame Grieve for the suggestion! - - - JPA module now supports deleting resource via transaction - - - DateClientParam#second() incorrectly used DAY precision instead - of SECOND precision. Thanks to Tom Wilson for the pull request! - - - Fix issue where HAPI failed to initialize correctly if Woodstox library was not on the classpath, even - if StAX API was configured to use a different provider. Thanks to - James Butler for reporting and figuring out where the issue was! - - - Calling BaseDateTimeDt#setValue(Date, TemporalPrecisionEnum) did not always actually respect - the given precision when the value was encoded. Thanks to jacksonjesse for - reporting! - - - Encoders (both XML and JSON) will no longer encode contained resources if they are - not referenced anywhere in the resource via a local reference. This is just a convenience - for users who have parsed a resource with contained resources and want to remove some - before re-encoding. Thanks to Alexander Kley for reporting! - - - Add support for DSTU2 style security labels in the parser and encoder. Thanks to - Mohammad Jafari for the contribution! - - - Server requests for Binary resources where the client has explicitly requested XML or JSON responses - (either with a _format]]> URL parameter, or an Accept]]> request header) - will be responded to using the Binary FHIR resource type instead of as Binary blobs. This is - in accordance with the recommended behaviour in the FHIR specification. - - - Add new properties to RestfulServer: "DefaultResponseEncoding", which allows - users to configure a default encoding (XML/JSON) to use if none is specified in the - client request. Currently defaults to XML. Also "DefaultPrettyPrint", which specifies - whether to pretty print responses by default. Both properties can be overridden - on individual requets using the appropriate Accept header or request URL parameters. - - - Add support for quantity search params in FHIR tester UI - - - Add support for FHIR "extended operations" as defined in the FHIR DSTU2 - specification, for the Generic Client, Annotation Client, and - Server. - - - Observation.applies[x] and other similar search fields with multiple allowable - value types were not being correctly indexed in the JPA server. - - - DateClientParam.before() incorrectly placed "<=" instead of - "<" in the request URL. Thanks to Ryan for reporting! - - - Server now only automatically adds _include resources which are provided - as references if the client request actually requested that specific include. - See RestfulServer - - - User defined resource types which contain extensions that use a bound code type - (e.g. an BoundCodeDt with a custom Enum) failed to parse correctly. Thanks - to baopingle for reporting and providing a test case! - - - Sorting is now supported in the Web Testing UI (previously a button existed for sorting, but it didn't do anything) - - - Server will no longer include stack traces in the OperationOutcome returned to the client - when an exception is thrown. A new interceptor called ExceptionHandlingInterceptor has been - created which adds this functionality back if it is needed (e.g. for DEV setups). See the - server interceptor documentation for more information. Thanks to Andy Huang for the suggestion! - -
    - - - API CHANGE:]]> The "FHIR structures" for DSTU1 (the classes which model the - resources and composite datatypes) have been moved out of the core JAR into their - own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast - version is finalized. See the - DSTU2 page]]> - for more information. - - - Deprocated API Removal: The following classes (which were deprocated previously) - have now been removed: -
      -
    • ISecurityManager: If you are using this class, the same functionality - is available through the more general purpose - server interceptor - capabilities. -
    • CodingListParam: This class was made redundant by the - TokenOrListParam - class, which can be used in its place. -
    - ]]> -
    - - API Change: The IResource#getResourceMetadata() method has been changed - from returning - Map<ResourceMetadataKeyEnum<?>, Object> - to returning a new type called - ResourceMetadataMap. This new type implements - Map<ResourceMetadataKeyEnum<?>, Object> - itself, so this change should not break existing code, but may - require a clean build in order to run correctly. - ]]> - - - Profile generation on the server was not working due to IdDt being - incorrectly used. Thanks to Bill de Beaubien for the pull request! - - - Profiles did not generate correctly if a resource definition class had a - defined extension which was of a composite type. Thanks to Bill de Beaubien for the pull request! - - - Remove unnecessary IOException from narrative generator API. Thanks to - Petro Mykhailysyn for the pull request! - - - Introduced a new - @ProvidesResources]]> annotation which can be added to - resource provider and servers to allow them to declare additional resource - classes they are able to serve. This is useful if you have a server which can - serve up multiple classes for the same resource type (e.g. a server that sometimes - returns a default Patient, but sometimes uses a custom subclass). - Thanks to Bill de Beaubien for the pull request! - - - Introduced a new - @Destroy]]> annotation which can be added to - a resource provider method. This method will be called by the server when it - is being closed/destroyed (e.g. when the application is being undeployed, the - container is being shut down, etc.) - Thanks to Bill de Beaubien for the pull request! - - - Add a new method to the server interceptor - framework which allows interceptors to be notified of any exceptions and - runtime errors within server methods. Interceptors may optionally also - override the default error handling behaviour of the RestfulServer. - - - Add constants to BaseResource for the "_id" search parameter which all resources - should support. - - - DateRangeParam parameters on the server now return correct - getLowerBoundAsInstant()]]> - and - getUpperBoundAsInstant()]]> - values if a single unqualified value is passed in. For example, if - a query containing - &birthdate=2012-10-01]]> - is received, previously these two methods would both return the same - value, but with this fix - getUpperBoundAsInstant()]]> - now returns the instant at 23:59:59.9999. - - - Resource fields with a type of "*" (or Any) sometimes failed to parse if a - value type of "code" was used. Thanks to Bill de Beaubien for reporting! - - - Remove dependency on JAXB libraries, which were used to parse and encode - dates and times (even in the JSON parser). JAXB is built in to most JDKs - but the version bundled with IBM's JDK is flaky and resulted in a number - of problems when deploying to Websphere. - - - Primitive datatypes now preserve their original string value when parsing resources, - as well as containing the "parsed value". For instance, a DecimalDt field value of - 1.0000]]> will be parsed into the corresponding - decimal value, but will also retain the original value with the corresponding - level of precision. This allows vadliator rules to be applied to - original values as received "over the wire", such as well formatted but - invalid dates, e.g. "2001-15-01". Thanks to Joe Athman for reporting and - helping to come up with a fix! - - - When using Generic Client, if performing a - or operation using a String as the resource body, - the client will auto-detect the FHIR encoding style and send an appropriate - header. - - - JPA module (and public HAPI-FHIR test server) were unable to process resource types - where at least one search parameter has no path specified. These now correctly save - (although the server does not yet process these params, and it should). Thanks to - GitHub user shvoidlee for reporting and help with analysis! - - - Generic/Fluent Client "create" and "update" method requests were not setting a content type header - - - DateDt left precision value as in the constructor - . - - - RESTful server now doesn't overwrite resource IDs if they are absolute. In other words, if - a server's Resource Provider returns a resource with ID "Patient/123" it will be translated to - "[base url]/Patient/123" but if the RP returns ID "http://foo/Patient/123" the ID will be - returned exactly as is. Thanks to Bill de Beaubien for the suggestion! - - - JPA module Transaction operation was not correctly replacing logical IDs - beginning with "cid:" with server assigned IDs, as required by the - specification. - - - did not visit or find children in contained resources when - searching a resource. This caused server implementations to not always return contained - resources when they are included with a resource being returned. - - - Add a method which returns the name of the - resource in question (e.g. "Patient", or "Observation"). This is intended as a - convenience to users. - - - Do not strip version from resource references in resources returned - from server search methods. Thanks to Bill de Beaubien for reporting! - - - Correct an issue with the validator where changes to the underlying - OperationOutcome produced by a validation cycle cause the validation - results to be incorrect. - - - Client interceptors registered to an interface based client instance - were applied to other client instances for the same client interface as well. (Issue - did not affect generic/fluent clients) - - - DateDt, DateTimeDt and types InstantDt types now do not throw an exception - if they are used to parse a value with the wrong level of precision for - the given type but do throw an exception if the wrong level of precision - is passed into their constructors.
    ]]> - This means that HAPI FHIR can now successfully parse resources from external - sources that have the wrong level of precision, but will generate a validation - error if the resource is validated. Thanks to Alexander Kley for the suggestion! -
    - - Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks - to Alexander Kley for reporting! - - - Server gives a more helpful error message if multiple IResourceProvider implementations - are provided for the same resource type. Thanks to wanghaisheng for the idea! - - - Bring DSTU1 resource definitions up to version 0.0.82-2929]]> - Bring DEV resource definitions up to 0.4.0-3775]]> - Thanks to crinacimpian for reporting! - - - JPA server did not correctly process _include requests if included - resources were present with a non-numeric identifier. Thanks to - Bill de Beaubien for reporting! - - - Client requests which include a resource/bundle body (e.g. create, - update, transaction) were not including a charset in the content type - header, leading to servers incorrectly assuming ISO-8859/1. Thanks to - shvoidlee for reporting! - - - Clean up the way that Profile resources are automatically exported - by the server for custom resource profile classes. See the - @ResourceDef]]> - JavaDoc for information on how this works. - - - Add convenience methods to TokenOrListParam to test whether any of a set of tokens match - the given requested list. - - - Add a protected method to RestfulServer which allows developers to - implement their own method for determining which part of the request - URL is the FHIR request path (useful if you are embedding the RestulServer inside - of another web framework). Thanks to Harsha Kumara for the pull request! - -
    - - - API CHANGE:]]> The TagList class previously implemented ArrayList semantics, - but this has been replaced with LinkedHashMap semantics. This means that the list of - tags will no longer accept duplicate tags, but that tag order will still be - preserved. Thanks to Bill de Beaubien for reporting! - - - Server was incorrectly including contained resources being returned as both contained resources, and as - top-level resources in the returned bundle for search operations. - Thanks to Bill de Beaubien for reporting! This also fixes Issue #20, thanks to - lephty for reporting! - - - Documentation fixes - - - Add a collection of new methods on the generic client which support the - read, - read, - and search - ]]> - operations using an absolute URL. This allows developers to perform these operations using - URLs they obtained from other sources (or external resource references within resources). In - addition, the existing read/vread operations will now access absolute URL references if - they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics - for contributing this implementation! - - - Server implementation was not correctly figuring out its own FHIR Base URL when deployed - on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of - Systems Made Simple for their help in figuring out this issue! - - - XML Parser failed to encode fields with both a resource reference child and - a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of - Systems Made Simple for their help in figuring out this issue! - - - HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to - Bernard Gitaadji for reporting and diagnosing the issue! - - - Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of - Orion Health for reporting this! - - - Conformance profiles which are automatically generated by the server were missing a few mandatory elements, - which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple - for reporting this! - - - XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters - escaped properly in encoded messages. - - - Resources containing entities which are not valid in basic XML (e.g. &sect;) will have those - entities converted to their equivalent unicode characters when resources are encoded, since FHIR does - not allow extended entities in resource instances. - - - Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers) - to client requests. - - - Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on - some platforms. Thanks to Odysseas Pentakalos for the patch! - - - HAPI now logs a single line indicating the StAX implementation being used upon the - first time an XML parser is created. - - - Update methods on the server did not return a "content-location" header, but - only a "location" header. Both are required according to the FHIR specification. - Thanks to Bill de Beaubien of Systems Made Simple for reporting this! - - - Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for - the patch! - - - Calling encode multiple times on a resource with contained resources caused the contained - resources to be re-added (and the actual message to grow) with each encode pass. Thanks to - Alexander Kley for the test case! - - - JSON-encoded contained resources with the incorrect "_id" element (which should be "id", but some - incorrect examples exist on the FHIR specification) now parse correctly. In other words, HAPI - previously only accepted the correct "id" element, but now it also accepts the incorrect - "_id" element just to be more lenient. - - - Several unit tests failed on Windows (or any platform with non UTF-8 default encoding). This may - have also caused resource validation to fail occasionally on these platforms as well. - Thanks to Bill de Beaubien for reporting! - - - toString() method on TokenParam was incorrectly showing the system as the value. - Thanks to Bill de Beaubien for reporting! - - - Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks - to David Hay of Orion Health for reporting! - - - Add a - Vagrant]]> - based environment (basically a fully built, self contained development environment) for - trying out the HAPI server modules. Thanks to Preston Lee for the pull request, and for - offering to maintain this! - - - Change validation API so that it uses a return type instead of exceptions to communicate - validation failures. Thanks to Joe Athman for the pull request! - - - Add a client interceptor which adds an HTTP cookie to each client request. Thanks to - Petro Mykhailysyn for the pull request! - - - - - - Add server interceptor framework, and new interceptor for logging incoming - requests. - - - Add server validation framework for validating resources against the FHIR schemas and schematrons - - - Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University - Health Network for reporting! - - - Create method was incorrectly returning an HTTP 204 on sucessful completion, but - should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng - for reporting! - - - FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing - non US-ASCII characters will correctly display in the browser - - - JSON parser was incorrectly encoding extensions on composite elements outside the element itself - (as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of - Orion for reporting this! - - - Contained/included resource instances received by a client are now automatically - added to any ResourceReferenceDt instancea in other resources which reference them. - - - Add documentation on how to use eBay CORS Filter to support Cross Origin Resource - Sharing (CORS) to server. CORS support that was built in to the server itself has - been removed, as it did not work correctly (and was reinventing a wheel that others - have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance - in testing this! - - - IResource interface did not expose the getLanguage/setLanguage methods from BaseResource, - so the resource language was difficult to access. - - - JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use - of single quotes - - - Transaction server method is now allowed to return an OperationOutcome in addition to the - incoming resources. The public test server now does this in order to return status information - about the transaction processing. - - - Update method in the server can now flag (via a field on the MethodOutcome object being returned) - that the result was actually a creation, and Create method can indicate that it was actually an - update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the - response, but this may be useful in some circumstances. - - - Annotation client search methods with a specific resource type (e.g. List<Patient> search()) - won't return any resources that aren't of the correct type that are received in a response - bundle (generally these are referenced resources, so they are populated in the reference fields instead). - Thanks to Tahura Chaudhry of University Health Network for the unit test! - - - Added narrative generator template for OperationOutcome resource - - - Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format - is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple - for reporting! - - - Server search method for an unnamed query gets called if the client requests a named query - with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting! - - - Category header (for tags) is correctly read in client for "read" operation - - - Transaction method in server can now have parameter type Bundle instead of - List<IResource> - - - HAPI parsers now use field access to get/set values instead of method accessors and mutators. - This should give a small performance boost. - - - JSON parser encodes resource references incorrectly, using the name "resource" instead - of the name "reference" for the actual reference. Thanks to - Ricky Nguyen for reporting and tracking down the issue! - - - Rename NotImpementedException to NotImplementedException (to correct typo) - - - Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status) - - - Fix performance issue in date/time datatypes where pattern matchers were not static - - - Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but - previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting! - - - Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health - for reporting! - - - QuantityParam correctly encodes approximate (~) prefix to values - - - If a server defines a method with parameter "_id", incoming search requests for that method may - get delegated to the wrong method. Thanks to Neal Acharya for reporting! - - - SecurityEvent.Object structural element has been renamed to - SecurityEvent.ObjectElement to avoid conflicting names with the - java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for - reporting! - - - Text/narrative blocks that were created with a non-empty - namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>) - failed to encode correctly (prefix was missing in encoded resource) - - - Resource references previously encoded their children (display and reference) - in the wrong order so references with both would fail schema validation. - - - SecurityEvent resource's enums now use friendly enum names instead of the unfriendly - numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the - suggestion! - - - - - HAPI has a number of RESTful method parameter types that have similar but not identical - purposes and confusing names. A cleanup has been undertaken to clean this up. - This means that a number of existing classes - have been deprocated in favour of new naming schemes. -
    ]]> - All annotation-based clients and all server search method parameters are now named - (type)Param, for example: StringParam, TokenParam, etc. -
    ]]> - All generic/fluent client method parameters are now named - (type)ClientParam, for example: StringClientParam, TokenClientParam, etc. -
    ]]> - All renamed classes have been retained and deprocated, so this change should not cause any issues - for existing applications but those applications should be refactored to use the - new parameters when possible. -
    - - Allow server methods to return wildcard generic types (e.g. List<? extends IResource>) - - - Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as - "&identifier=system|codepart1\|codepart2" - - - Add support for OPTIONS verb (which returns the server conformance statement) - - - Add support for CORS headers in server - - - Bump SLF4j dependency to latest version (1.7.7) - - - Add interceptor framework for clients (annotation based and generic), and add interceptors - for configurable logging, capturing requests and responses, and HTTP basic auth. - - - Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead - of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one! - - - Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion! - - - If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or - an OperationOutcome resource, include the message in the exception message so that it will be - more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion! - - - Read invocations in the client now process the "Content-Location" header and use it to - populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion! - - - Fix issue where vread invocations on server incorrectly get routed to instance history method if one is - defined. Thanks to Neal Acharya from UHN for surfacing this one! - - - Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary - blobs from being used for nefarious purposes. See - FHIR Tracker Bug 3298]]> - for more information. - - - Support has been added for using an HTTP proxy for outgoing requests. - - - Fix: Primitive extensions declared against custom resource types - are encoded even if they have no value. Thanks to David Hay of Orion for - reporting this! - - - Fix: RESTful server deployed to a location where the URL to access it contained a - space (e.g. a WAR file with a space in the name) failed to work correctly. - Thanks to David Hay of Orion for reporting this! - -
    - - - BREAKING CHANGE:]]>: IdDt has been modified so that it - contains a partial or complete resource identity. Previously it contained - only the simple alphanumeric id of the resource (the part at the end of the "read" URL for - that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123") - and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have - been added to this datatype which provide just the numeric portion. See the JavaDoc - for more information. - - - API CHANGE:]]>: Most elements in the HAPI FHIR model contain - a getId() and setId() method. This method is confusing because it is only actually used - for IDREF elements (which are rare) but its name makes it easy to confuse with more - important identifiers. For this reason, these methods have been deprocated and replaced with - get/setElementSpecificId() methods. The old methods will be removed at some point. Resource - types are unchanged and retain their get/setId methods. - - - Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously - QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to - support quantity search parameters on the server (e.g. Observation.value-quantity) - - - Introduce StringParameter type which can be used as a RESTful operation search parameter - type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers. - - - Parsers (XML/JSON) now support deleted entries in bundles - - - Transaction method now supported in servers - - - Support for Binary resources added (in servers, clients, parsers, etc.) - - - Support for Query resources fixed (in parser) - - - Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource) - now parse and encode correctly, meaning that all contained resources are placed in the "contained" element - of the root resource, and the parser looks in the root resource for all container levels when stitching - contained resources back together. - - - Server methods with @Include parameter would sometimes fail when no _include was actually - specified in query strings. - - - Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct - query string if the system is null. - - - Add support for paging responses from RESTful servers. - - - Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are - produced by the Health Intersections server) - - - Server now automatically compresses responses if the client indicates support - - - Server failed to support optional parameters when type is String and :exact qualifier is used - - - Read method in client correctly populated resource ID in returned object - - - Support added for deleted-entry by/name, by/email, and comment from Tombstones spec - - - - - - - - - - + + + + James Agnew + HAPI FHIR Changelog + + + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Hibernate (JPA): 5.1.0 -> 5.2.7
  • +
  • Hibernate Search (JPA): 5.5.4 ->p; 5.7.0.CR1
  • +
  • Hibernate Validator (JPA): 5.2.4 ->p; 5.3.4
  • +
  • Spring (JPA): 4.3.1 -> 4.3.6
  • + + ]]> +
    + + The JPA server now supports custom search parameters in DSTU3 + mode. This allows users to create search parameters which contain + custom paths, or even override and disable existing search + parameters. + + + CLI example uploader couldn't find STU3 examples after CI server + was moved to build.fhir.org + + + Fix issue in JPA subscription module that prevented purging stale + subscriptions when many were present on Postgres + + + Server interceptor methods were being called twice unnecessarily + by the JPA server, and the DaoConfig interceptor registration + framework was not actually useful. Thanks to GitHub user + @mattiuusitalo for reporting! + + + AuthorizationInterceptor on JPA server did not correctly + apply rules on deleting resources in a specific compartment + because the resource metadata was stripped by the JPA server + before the interceptor could see it. Thanks to + Eeva Turkka for reporting! + + + JPA server exported CapabilityStatement includes + double entries for the _id parameter and uses the + wrong type (string instead of token). Thanks to + Robert Lichtenberger for reporting! + + + Custom resource types which extend Binary must not + have declared extensions since this is invalid in + FHIR (and HAPI would just ignore them anyhow). Thanks + to Thomas S Berg for reporting! + + + Standard HAPI zip/tar distributions did not include the project + sources and JavaDoc JARs. Thanks to Keith Boone for pointing + this out! + + + Server AuthorizationInterceptor always rejects history operation + at the type level even if rules should allow it. + + + JPA server terminology service was not correctly validating or expanding codes + in SNOMED CT or LOINC code systems. Thanks to David Hay for reporting! + + + Attempting to search for an invalid resource type (e.g. GET base/FooResource) should + return an HTTP 404 and not a 400, per the HTTP spec. Thanks to + GitHub user @CarthageKing for the pull request! + + + When parsing a Bundle containing placeholder fullUrls and references + (e.g. "urn:uuid:0000-0000") the resource reference targets did not get + populated with the given resources. Note that as a part of this + change, IdType and IdDt]]> have been modified + so that when parsing a placeholder ID, the complete placeholder including the + "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, + the prefix was treated as the base URL, which led to strange behaviour + like the placeholder being treated as a real IDs. Thanks to GitHub + user @jodue for reporting! + + + Declared extensions with multiple type() options listed in the @Child + annotation caused a crash on startup. Now this is supported. + + + STU3 XHTML parser for narrative choked if the narrative contained + an &rsquot;]]> entity string. + + + When parsing a quantity parameter on the server with a + value and units but no system (e.g. + GET [base]/Observation?value=5.4||mg]]>) + the unit was incorrectly treated as the system. Thanks to + @CarthageKing for the pull request! + + + Correct a typo in the JPA ValueSet ResourceProvider which prevented + successful operation under Spring 4.3. Thanks to + Robbert van Waveren for the pull request! + + + Deprecate the method + ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> + as it is no longer used by HAPI and is just an annoying step + in creating custom structures. Thanks to Allan Bro Hansen + for pointing this out. + + + CapturingInterceptor did not buffer the response meaning + that in many circumstances it did not actually capture + the response. Thanks to Jenny Syed of Cerner for + the pull request and contribution! + + + Clean up dependencies and remove Eclipse project files from git. Thanks to + @sekaijin for the pull request! + + + When performing a conditional create in a transaction in JPA server, + if a resource already existed matching the conditional expression, the + server did not change the version of the resource but did update the body + with the passed in body. Thanks to Artem Sopin for reporting and providing a test + case for this! + + + Client revincludes did not include the :recurse modifier. Thanks to + Jenny Meinsma for pointing this out on Zulip! + + + JPA server did not return an OperationOutcome in the response for + a normal delete operation. + + + Fix an issue in JPA server where _history results were kept in memory instead + of being spooled to the database as they should be. Note that as a part of this fix + a new method was added to + IBundleProvider called getUuid()]]>. This + method may return null]]> in any current cases. + + + Expanding a ValueSet in JPA server did not correctly apply + ?filter=]]> parameter when the ValueSet + being expanded had codes included explicitly (i.e. not by + is-a relationship). Thanks to David Hay for reporting! + + + JPA validator incorrectly returned an HTTP 400 instead of an HTTP 422 when + the resource ID was not present and required, or vice versa. Thanks to + Brian Postlethwaite for reporting! + +
    + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + + +
  • Derby (CLI): 10.12.1.1 -> 10.13.1.1
  • +
  • Jetty (CLI): 9.3.10.v20160621 -> 9.3.14.v20161028
  • +
  • JAnsi (CLI): 1.13 -> 1.14
  • +
  • Phloc Commons (SCH Validator): 4.4.5 -> 4.4.6
  • + + ]]> +
    + + Fix issue in AuthorizationIntetceptor where + transactions are blocked even when they + should not be + + + Fix regression in HAPI FHIR 2.1 JPA + server where some search parameters on + metadata resources did not appear + (e.g. "StructureDefinition.url"). Thanks + to David Hay for reporting! + + + Add ability to JPA server for disabling stale search + expiry. This is useful if you are deploying the server + to a cluster. + + + RestfulServer with no explicitly set FhirContext + fails to detect the presents of DSTU3 structures. Thanks + to GitHub user @vijayt27 for reporting! + + + As the + eBay CORS interceptor]]> + project + has gone dormant, we have introduced a new + HAPI server interceptor which can be used to implement CORS support + instead of using the previously recommended Servlet Filter. All server + examples as well as the CLI have been switched to use this new interceptor. + See the + CORS Documentation]]> + for more information. + + + Make the parser configurable so that when + parsing an invalid empty value (e.g. + {"status":""}]]>) the + parser will either throw a meaningful exception + or log a warning depending on the configured + error handler. + + + Fix issue when serializing resources that have + contained resources which are referred to + from multiple places. Sometimes when serializing + these resources the contained resource section + would contain duplicates. Thanks to Hugo Soares + and Stefan Evinance for reporting and providing + a test case! + + + Allow client to gracefully handle running in DSTU3 mode + but with a structures JAR that does not contain a + CapabilityStatement resource. Thanks to Michael Lawley + for the pull request! + + + Fix a crash in JPA server when searching using an _include if _include targets are + external references (and therefore can't be loaded + by the server). Thanks to Hannes Ulrich for reporting! + + + HAPI FHIR CLI failed to delete a file when uploading + example resources while running under Windows. + + + Server should reject update if the resource body + does not contain an ID, or the ID does not match + the request URL. Thanks to Jim Steel for reporting! + + + Web Testing UI's next and previous buttons for paging + through paged results did not work after the migration + to using Thymeleaf 3. Thanks to GitHub user @gsureshkumar + for reporting! + + + When parsing invalid enum values in STU3, + report errors through the parserErrorHandler, + not by throwing an exception. Thanks to + Michael Lawley for the pull request! + + + When parsing DSTU3 resources with enumerated + types that contain invalid values, the parser will now + invoke the parserErrorHandler. For example, when parsing + {"resourceType":"Patient", "gender":"foo"} + ]]> + the previous behaviour was to throw an InvalidArgumentException. + Now, the parserErrorHandler is invoked. In addition, thw + LenientErrorHandler has been modified so that this one case + will result in a DataFormatException. This has the effect + that servers which receive an invalid enum velue will return + an HTTP 400 instead of an HTTP 500. Thanks to Jim + Steel for reporting! + + + DSTU3 context now pulls the FHIR version from the actual + model classes. Thanks to Michael Lawley for the pull request! + + + Enhancements to the tinder-plugin's generic template features + of the generate-multi-files and generate-single-file + Maven goals as well as the Ant hapi-tinder task. +
      +
    • Provides the full Tinder data model by adding composites, valuesets, and profiles to resourcesw.
    • +
    • Supports generating files for resources, composites, valuesets, and profiles
    • +
    • Supports Velocimacro files outside the tinder-plugin JAR
    • +
    • Provides filename prefix as well as suffix properties
    • +
    • Can specify any of the Velocity configuration parameters such as + macro.provide.scope.control which allows safe macro recursion
    • +
    • Templates can now drill down into the referenced children for a ResourceBlockCopy
    • +
    • Normalization of properties across all three generic tasks
    • +
    + ]]> +
    + + Fix ordering of validator property handling when an element + has a name that is similar to a shorter name[x] style name. + Thanks to CarthageKing for the pull request! + + + Add a docker configuration to the hapi-fhir-jpaservr-example + module. Thanks to Gijsbert van den Brink for the pull request! + + + Add utility constructors to MoneyDt. Thanks to James Ren for the + contribution! + + + AuthorizationInterceptor was failing to allow read requests to pass + when a rule authorized those resources by compartment. Thanks to + GitHub user @mattiuusitalo for reporting and supplying + a test case! + + + Correct a typo in client + IHttpRequest]]> class: "bufferEntitity" should be "bufferEntity". + + + ErrorHandler is now called (resulting in a warning by default, but can also be an exception) when arsing JSON if + the resource ID is not a JSON string, or an object is found where an array is expected (e.g. repeating field). Thanks + to Jenni Syed of Cerner for providing a test case! + + + Fix Web Testing UI to be able to handle STU3 servers which + return CapabilityStatement instead of the previously used + "Conformance" resource + + + CLI example uploader couldn't find STU3 examples after CI server + was moved to build.fhir.org + + + Fix issue in JPA subscription module that prevented purging stale + subscriptions when many were present on Postgres + + + Server interceptor methods were being called twice unnecessarily + by the JPA server, and the DaoConfig interceptor registration + framework was not actually useful. Thanks to GitHub user + @mattiuusitalo for reporting! + + + AuthorizationInterceptor on JPA server did not correctly + apply rules on deleting resources in a specific compartment + because the resource metadata was stripped by the JPA server + before the interceptor could see it. Thanks to + Eeva Turkka for reporting! + + + JPA server exported CapabilityStatement includes + double entries for the _id parameter and uses the + wrong type (string instead of token). Thanks to + Robert Lichtenberger for reporting! + + + Custom resource types which extend Binary must not + have declared extensions since this is invalid in + FHIR (and HAPI would just ignore them anyhow). Thanks + to Thomas S Berg for reporting! + + + Standard HAPI zip/tar distributions did not include the project + sources and JavaDoc JARs. Thanks to Keith Boone for pointing + this out! + + + Server AuthorizationInterceptor always rejects history operation + at the type level even if rules should allow it. + + + JPA server terminology service was not correctly validating or expanding codes + in SNOMED CT or LOINC code systems. Thanks to David Hay for reporting! + + + Attempting to search for an invalid resource type (e.g. GET base/FooResource) should + return an HTTP 404 and not a 400, per the HTTP spec. Thanks to + GitHub user @CarthageKing for the pull request! + + + When parsing a Bundle containing placeholder fullUrls and references + (e.g. "urn:uuid:0000-0000") the resource reference targets did not get + populated with the given resources. Note that as a part of this + change, IdType and IdDt]]> have been modified + so that when parsing a placeholder ID, the complete placeholder including the + "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, + the prefix was treated as the base URL, which led to strange behaviour + like the placeholder being treated as a real IDs. Thanks to GitHub + user @jodue for reporting! + + + Declared extensions with multiple type() options listed in the @Child + annotation caused a crash on startup. Now this is supported. + + + STU3 XHTML parser for narrative choked if the narrative contained + an &rsquot;]]> entity string. + + + When parsing a quantity parameter on the server with a + value and units but no system (e.g. + GET [base]/Observation?value=5.4||mg]]>) + the unit was incorrectly treated as the system. Thanks to + @CarthageKing for the pull request! + + + Correct a typo in the JPA ValueSet ResourceProvider which prevented + successful operation under Spring 4.3. Thanks to + Robbert van Waveren for the pull request! + + + Deprecate the method + ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> + as it is no longer used by HAPI and is just an annoying step + in creating custom structures. Thanks to Allan Bro Hansen + for pointing this out. + + + CapturingInterceptor did not buffer the response meaning + that in many circumstances it did not actually capture + the response. Thanks to Jenny Syed of Cerner for + the pull request and contribution! + +
    + + + STU3 structure definitions have been updated to the + STU3 latest definitions (1.7.0 - SVN 10129). In + particular, this version supports the new CapabilityStatement + resource which replaces the previous Conformance + resource (in order to reduce upgrade pain, both resource + types are included in this version of HAPI) + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • spring-data-orm (JPA): 1.10.2 -> 1.10.4
  • + + ]]> +
    + + Fix a fairly significant issue in JPA Server when using the DatabaseBackedPagingProvider]]>: When paging over the results + of a search / $everything operation, under certain circumstances resources may be missing from the last page of results + that is returned. Thanks to David Hay for reporting! + + + Client, Server, and JPA server now support experimental support + for + + using the XML Patch and JSON Patch syntax as explored during the + September 2016 Baltimore Connectathon. See + this wiki page]]> + for a description of the syntax. + ]]> + Thanks to Pater Girard for all of his help during the connectathon + in implementing this feature! + + + Android library now uses OkHttp client by default instead + of Apache HttpClient. This should lead to much simpler + support for Android in the future. + + + Both client and server now use the new STU3 mime types by default + if running in STU3 mode (in other words, using an STU3 + FhirContext). + + + In server, when returning a list of resources, the server sometimes failed to add + _include]]> resources to the response bundle if they were + referred to by a contained reosurce. Thanks to Neal Acharya for reporting! + + + Fix regression in web testing UI where "prev" and "next" buttons don't work + when showing a result bundle + + + JPA server should not attempt to resolve built-in FHIR StructureDefinitions from the + database (this causes a significant performance hit when validating) + + + BanUnsupportedHttpMethodsInterceptor was erroring out when a client + attempts HTTP HEAD requests + + + Conditional URLs in JPA server (e.g. for delete or update) did not support the + _has]]> parameter + + + Remove Maven dependency on Saxon library, as it is not actually used. Thanks + to Lem Edmondson for the suggestion! + + + Times before 1970 with fractional milliseconds were parsed incorrectly. Thanks + to GitHub user @CarthageKing for reporting! + + + Prevent crash in parser when parsing resource + with multiple profile declarations when + default type for profile is used. Thanks to + Filip Domazet for the pull request! + + + STU3 servers were adding the old MimeType + strings to the + Conformance.format]]> + part of the generated server conformance + statement + + + When performing an update using the client on a resource that + contains other resources (e.g. Bundle update), all child resources in the + parent bundle were incorrectly given the ID of the parent. Thanks + to Filip Domazet for reporting! + + + STU clients now use an Accept header which + indicates support for both the old MimeTypes + (e.g. application/xml+fhir]]>) + and the new MimeTypes + (e.g. application/fhir+xml]]>) + + + JPA server now sends correct + HTTP 409 Version Conflict]]> + when a + DELETE fails because of constraint issues, instead of + HTTP 400 Invalid Request]]> + + + Server history operation did not populate the Bundle.entry.request.url + field, which is required in order for the bundle to pass validation. + Thanks to Richard Ettema for spotting this! + + + Add a new method to the server interceptor framework which will be + called after all other processing is complete (useful for performance + tracking). The server LoggingInterceptor has been switched to using this + method which means that log lines will be created when processing is finished, + instead of when it started. + + + STU3 clients were not sending the new mimetype values in the + Content-Type]]> header. Thanks to + Claude Nanjo for pointing this out! + + + JAX-RS server was not able to handle the new mime types defined + in STU3 + + + JPA server did not handle custom types when being called + programatically (I.e. not through HTTP interface). Thanks to + Anthony Mei for pointing this out! + + + CLI was not correctly able to upload DSTU2 examples to any server + + + STU3 validator has been upgrated to include fixes made since the + 1.6.0 ballot + + + Prevent JPA server from creating a bunch of + FhirContext objects for versions of FHIR that + aren't actually being used + + + XhtmlNode.equalsDeep() contained a bug which caused resources + containing a narrative to always return + false]]> for STU3 + Resource#equalsDeep()]]>. Thanks to + GitHub user @XcrigX for reporting! + + + JPA server did not correctly process searches for chained parameters + where the chain passed across a field that was a choice between a + reference and a non-reference type (e.g. + MedicationAdministration.medication[x]]]>. + Thanks to GitHub user @Crudelus for reporting! + + + Handle parsing an extension without a URL more gracefully. In HAPI FHIR 2.0 this caused + a NullPointerException to be thrown. Now it will trigger a warning, or throw a + DataFormatException if the StrictErrorHandler is configured on the parser. + + + Calling a HAPI server URL with a chain on a parameter that shouldn't accept + chains (e.g. + GET [base]/Patient?name.foo=smith]]>) + did not return an error and instead just ignored the chained part + and treated the parameter as though it did not have the chain. This + led to confusing and potentially unsafe behaviour. This has been + corrected to return an error to the client. Thanks to + Kevin Tallevi for finding this! + + + Fix #411 - Searching by POST [base]/_search]]> with urlencoded parameters doesn't work correctly if + interceptors are accessing the parameters and there is are also + parameters on the URL. Thanks to Jim Steel for reporting! + + + Fluent client can now return types other than Parameters + when invoking operations. + + + JPA server shouldn't report a totalCount in Bundle of "-1" when + there are no results + + + JPA server was not correctly normalizing strings with non-latin characters + (e.g. Chinese chars). Thanks to GitHub user @YinAqu for reporting and providing + some great analysis of the issue! + + + Add a new method to ReferenceClientParam which allows you to + pass in a number of IDs by a collection of Strings. Thanks to + Thomas Andersen for the pul request! + + + When encoding a resource in JSON where the resource has + an extension with a value where the value is a reference to a + contained resource, the reference value (e.g. "#1") did not + get serialized. Thanks to GitHub user @fw060 for reporting! + + + ResponseHighlighterInterceptor now pretty-prints responses + by default unless the user has explicitly requested + a non-pretty-printed response (ie. + using ?_pretty=false]]>. Thanks to + Allan Brohansen and Jens Villadsen for the suggestion! + + + Add a new JSON library abstraction layer to the JSON parser. + This contribution shouldn't have any end-user impact but does + make it easier to use the JSON parser to generate custom structures + for other purposes, and should allow us to support RDF more + easily at some point. Thanks to Bill Denton for the pull + request and the contribution! + + + DSTU1 Bundle encoder did not include the Bundle entry author in + the generated bundle. Thanks to Hannes Venter for the pull + request and contribution! + + + Remove unused field (myIsContained) from ResourceTable + in JPA server. + + + AuthorizationInterceptor is now a bit more aggressive + at blocking read operations, stopping them on the + way in if there is no way they will be accepted + to the resource check on the way out. In addition + it can now be configured to allow/deny operation + invocations at the instance level on any + instance of a given type + + + STU3 servers were incorrectly returning the + Content-Location]]> + header instead of the + Content]]> + header. The former has been removed from the + FHIR specification in STU3, but the + latter got removed in HAPI's code base. + Thanks to Jim Steel for reporting! + + + Correct several documentation issues. Thanks to Vadim Peretokin + for the pull requests! + + + Remove an unneccesary database flush + from JPA persistence operations + + + Add method to fluent client to allow OR search across several + profiles. Thanks to Thomas Andersen for the pull request! + +
    + + + JSON parsing in HAPI FHIR has been switched from using JSR353 (javax.json) to + using Google Gson. For this reason we are bumping the major release number to + 2.0. Theoretically this should not affect projects in any major way, but Gson + does have subtle differences. Two differences which popped up a fair bit in + our own testing: + +
      + A space is placed after the : in keys, e.g. what was previously + encoded as "resourceType":"Patient" is now encoded + as "resourceType": "Patient" (this broke a number of + our unit tests with hardcoded resource definitions) +
    +
      + Trailing content after a valid json resource is rejected by + Gson (it was ignored by the Glassfish parser we were previously + using even though it was invalid) +
    + + ]]> +
    + + STU3 structure definitions have been updated to the + STU3 ballot candidate versions (1.6.0 - SVN 9663) + + + Both client and server now support the new Content Types decided in + FHIR #10199]]>. +
    ]]> + This means that the server now supports + application/fhir+xml and application/fhir+json]]> + in addition to the older style + application/xml+fhir and application/json+fhir]]>. + In order to facilitate migration by implementors, the old style remains the default + for now, but the server will respond using the new style if the request contains it. The + client now uses an Accept]]> header value which requests both + styles with a preference given to the new style when running in DSTU3 mode. +
    ]]> + As a part of this change, the server has also been enhanced so that if a request + contains a Content-Type header but no Accept header, the response will prefer the + encoding specified by the Content-Type header. +
    + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Logback (used in sample projects): 1.1.5 -> 1.1.7
  • +
  • Phloc Commons (used by schematron validator): 4.4.4 -> 4.4.5
  • +
  • Commons-IO: 2.4 -> 2.5
  • +
  • Apache HTTPClient: 4.5.1 -> 4.5.2
  • +
  • Apache HTTPCore: 4.4.4 -> 4.4.5
  • +
  • Jersey (JAX-RS tests): 2.22.2 -> 2.23.1
  • +
  • Spring (JPA, Web Tester): 4.3.0 -> 4.3.1
  • + +
  • Hibernate Search (JPA): 5.5.2 -> 5.5.4
  • +
  • Thymeleaf (Narrative Generator / Web Tester): 2.1.4 ->3.0.1
  • + + ]]> +
    + + + Fix issue in DSTU1 Bundle parsing where unexpected elements in the bundle resulted in a failure + to parse. + + + DSTU2 QuestionnaireResponse validator failed with an exception if the + QuestionnaireResponse contained certain groups with no content + + + Fluent client should ignore parameter values which are null instead of including + them as ?foo=null]]> + + + When using _elements]]> parameter on server, the server was not + automatically adding the SUBSETTED]]> tag as it should + + + JPA server should now automatically detect + if Hibernate Search (Lucene) is configured to be + disabled and will not attempt to use it. This + prevents a crash for some operations. + + + A new server interceptor "BanUnsupprtedHttpMethodsInterceptor" has been added + which causes the server to return an HTTP 405 if an unsupported HTTP + verb is received from the client + + + Fix an issue where resource IDs were not correctly set when using + DSTU2 HL7org structures with the JAX-RS module. Thanks to Carlo Mion + for the pull request! + + + hapi-fhir-testpage-overlay project contained an unneccesary + dependency on hapi-fhir-jpaserver-base module, which resulted in + projects using the overlay having a large number of unnneded + JARs included + + + It is not possible to configure both the parser and the context to + preserve versions in resource references (default behaviour is to + strip versions from references). Thanks to GitHub user @cknaap + for the suggestion! + + + Tag#setCode(String)]]> did not actually set the code it was supposed to + set. Thanks to Tim Tschampel for reporting! + + + JPA server's /Bundle]]> endpoint cleared + the Bundle.entry.fullUrl]]> field on stored + bundles, resulting in invalid content being saved. Thanks to Mirjam + Baltus for reporting! + + + JPA server now returns HTTP 200 instead of HTTP 404 for + conditional deletes which did not find any matches, + per FHIR-I decision. + + + Client that declares explicitly that it is searching/reading/etc for + a custom type did not automatically parse into that type. + + + Allow servers to specify the authentication realm of their choosing when + throwing an AuthenticationException. Thanks to GitHub user @allanbrohansen + for the suggestion! + + + Add a new client implementation which uses the + OkHttp]]> + library as the HTTP client implementation (instead of Apache HttpClient). + This is particularly useful for Android (where HttpClient is a pain) but + could also be useful in other places too. + Thanks to Matt Clarke of Orion Health for the contribution! + + + Fix a regression when parsing resources that have contained + resources, where the reference in the outer resource which + links to the contained resource sometimes did does not get + populated with the actual target resource instance. Thanks to + Neal Acharya for reporting! + + + hapi-fhir-cli upload-terminology command now has an argument + "-b FOO" that lets you add an authorization header in the form + Authorization: Bearer FOO]]> + + + Parser failed to successfully encode a custom resource + if it contained custom fields that also used custom + types. Thanks to GitHub user @sjanic for reporting! + + + Inprove handling of _text and _content searches in JPA server to do better + matching on partial strings + + + Servers in STU3 mode will now ignore any ID or VersionID found in the + resource body provided by the client when processing FHIR + update]]> operations. This change has been made + because the FHIR specification now requires servers to ignore + these values. Note that as a result of this change, resources passed + to @Update]]> methods will always have + null]]> ID + + + Add new methods to + AuthorizationInterceptor]]> + which allow user code to declare support for conditional + create, update, and delete. + + + When encoding a resource with a reference to another resource + that has a placeholder ID (e.g. urn:uuid:foo), the urn prefix + was incorrectly stripped from the reference. + + + Servers for STU3 (or newer) will no longer include a + Location:]]> header on responses for + read]]> operations. This header was + required in earlier versions of FHIR but has been removed + from the specification. + + + Fix NullPointerException when encoding an extension containing CodeableConcept + with log level set to TRACE. Thanks to Bill Denton for the report! + + + Add two new methods to the parser error handler that let users trap + invalid contained resources with no ID, as well as references to contained + resource that do not exist. + + + Improve performance when parsing resources containing contained resources + by eliminating a step where references were woven twice + + + Parser failed to parse resources containing an extension with a value type of + "id". Thanks to Raphael Mäder for reporting! + + + When committing a transaction in JPA server + where the transaction contained placeholder IDs + for references between bundles, the placeholder + IDs were not substituted with viewing + resources using the _history operation + + + HAPI root pom shouldn't include animal-sniffer plugin, + since that causes any projects which extend this to + be held to Java 6 compliance. + +
    + + + Performance has been improved for the initial FhirContext + object creation by avoiding a lot of unnecessary reflection. HAPI FHIR + 1.5 had a regression compared to previous releases + and this has been corrected, but other improvements have been + made so that this release is faster than previous releases too. +
    ]]> + In addition, a new "deferred scan" mode has been implemented for + even faster initialization on slower environments (e.g. Android). + See the performance documentation]]> + for more information. +
    ]]> + The following shows our benchmarks for context initialization across several + versions of HAPI: + +
  • Version 1.4: 560ms
  • +
  • Version 1.5: 800ms
  • +
  • Version 1.6: 340ms
  • +
  • Version 1.6 (deferred mode): 240ms
  • + + ]]> +
    + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Spring (JPA, Web Tester): 4.2.5 -> 4.3.0
  • +
  • Spring-Data (JPA): 1.9.2 -> 1.10.1
  • + +
  • Hibernate Search (JPA): 5.5.2 -> 5.5.3
  • +
  • Jetty (CLI): 9.3.9 -> 9.3.10
  • + + ]]> +
    + + Remove some clases that were deprecated over a year ago and have + suitable replacements: + +
  • QualifiedDateParam has been removed, but DateParam may be used instead
  • +
  • PathSpecification has been removedm but Include may be used instead
  • + + ]]> +
    + + ResponseValidatingInterceptor threw an InternalErrorException (HTTP 500) for operations + that do not return any content (e.g. delete). Thanks to Mohammad Jafari for reporting! + + + REST server now throws an HTTP 400 instead of an HTTP 500 if an operation which takes + a FHIR resource in the request body (e.g. create, update) contains invalid content that + the parser is unable to parse. Thanks to Jim Steel for the suggestion! + + + Deprecate fluent client search operations without an explicit declaration of the + bundle type being used. This also means that in a client + .search()]]> + operation, the + .returnBundle(Bundle.class)]]> + needs to be the last statement before + .execute()]]> + + + Server now respects the parameter _format=application/xml+fhir"]]> + which is technically invalid since the + should be escaped, but is likely to be used. Also, + a parameter of _format=html]]> can now be used, which + forces SyntaxHighlightingInterceptor to use HTML even + if the headers wouldn't otherwise trigger it. + Thanks to Jim Steel for reporting! + + + Improve performance when parsing large bundles by fixing a loop over all of the + entries inthe bundle to stitch together cross-references, which was happening once + per entry instead of once overall. Thanks to Erick on the HAPI FHIR Google Group for + noticing that this was an issue! + + + JSON parser no longer allows the resource ID to be specified in an element called "_id" + (the correct one is "id"). Previously _id was allowed because some early FHIR examples + used that form, but this was never actually valid so it is now being removed. + + + JPA server now allows "forced IDs" (ids containing non-numeric, client assigned IDs) + to use the same logical ID part on different resource types. E.g. A server may now have + both Patient/foo and Obervation/foo on the same server.
    ]]> + Note that existing databases will need to modify index "IDX_FORCEDID" as + it is no longer unique, and perform a reindexing pass. +
    + + When serializing/encoding custom types which replace exsting choice fields by + fixing the choice to a single type, the parser would forget that the + field was a choice and would use the wrong name (e.g. "abatement" instead of + "abatementDateType"). Thanks to Yaroslav Kovbas for reporting and + providing a unit test! + + + JPA server transactions sometimes created an incorrect resource reference + if a resource being saved contained references that had a display value but + not an actual reference. Thanks to David Hay for reporting! + + + When performing a REST Client create or update with + Prefer: return=representation]]> set, + if the server does not honour the Prefer header, the client + will automatically fetch the resource before returning. Thanks + to Ewout Kramer for the idea! + + + DSTU3 structures now have + setFoo(List)]]> + and + setGetFooFirstRep()]]> + methods, bringing them back to parity with the HAPI + DSTU2 structures. Thanks to Rahul Somasunderam and + Claude Nanjo for the suggestions! + + + JPA server has now been refactored to use the + new FluentPath search parameter definitions + for DSTU3 resources. + + + RequestValidatingInterceptor and ResponseValidatingInterceptor + both have new method setIgnoreValidatorExceptions]]> + which causes validator exceptions to be ignored, rather than causing + processing to be aborted. + + + LoggingInterceptor on server has a new parameter + ${requestBodyFhir}]]> which logs the entire request body. + + + JAX-RS server module now supports DSTU3 resources (previously it only supported DSTU2). Thanks + to Phillip Warner for implementing this, and providing a pull request! + + + Generated conformance statements for DSTU3 servers did not properly reference their + OperationDefinitions. Thanks + to Phillip Warner for implementing this, and providing a pull request! + + + Properly handle null arrays when parsing JSON resources. Thanks to Subhro for + fixing this and providing a pull request! + + + STU3 validator failed to validate codes where the + code was a child code within the code system that contained it + (i.e. not a top level code). Thanks to Jon + Zammit for reporting! + + + Restore the setType method in the DSTU1 Bundle + class, as it was accidentally commented out. Thanks + to GitHub user @Virdulys for the pull request! + + + JPA server now supports composite search parameters + where the type of the composite parameter is + a quantity (e.g. Observation:component-code-component-value-quantity) + + + Remove the Remittance resource from DSTU2 + structures, as it is not a real resource and + was causing issues with interoperability + with the .NET client. + + + CLI tool cache feature (-c) for upload-example task sometimes failed + to write cache file and exited with an exception. + + + Fix error message in web testing UI when loading pages in a search + result for STU3 endpoints. + + + When encoding JSON resource, the parser will now always + ensure that XHTML narrative content has an + XHTML namespace declaration on the first + DIV tag. This was preventing validation for + some resources using the official validator + rules. + + + Server failed to invoke operations when the name + was escaped (%24execute instead of $execute). + Thanks to Michael Lawley for reporting! + + + JPA server transactions containing a bundle that has multiple entries + trying to delete the same resource caused a 500 internal error + + + JPA module failed to index search parameters that mapped to a Timing datatype, + e.g. CarePlan:activitydate + + + Add a new option to the CLI run-server command called --lowmem]]>. + This option disables some features (e.g. fulltext search) in order to allow the + server to start in memory-constrained environments (e.g Raspberry Pi) + + + When updating a resource via an update operation on the server, if the ID of the + resource is not present in the resource body but is present on the URL, this will + now be treated as a warning instead of as a failure in order to be a bit more + tolerant of errors. If the ID is present in the body but does not agree with the + ID in the URL this remains an error. + + + Server / JPA server date range search params (e.g. Encounter:date) now treat + a single date with no comparator (or the eq comparator) as requiring that the + value be completely contained by the range specified. Thanks to Chris Moesel + for the suggestion. + + + In server, if a parameter was annotated with the annotation, the + count would not appear in the self/prev/next links and would not actually be applied + to the search results by the server. Thanks to Jim Steele for letting us know! + + + Conditional update on server failed to process if the conditional URL did not have any + search parameters that did not start with an underscore. E.g. "Patient?_id=1" failed + even though this is a valid conditional reference. + + + JPA server can now be configured to allow external references (i.e. references that + point to resources on other servers). See + JPA Documentation]]> for information on + how to use this. Thanks to Naminder Soorma for the suggestion! + + + When posting a resource to a server that contains an invalid value in a boolean field + (e.g. Patient with an active value of "1") the server should return an HTTP 400, not + an HTTP 500. Thanks to Jim Steel for reporting! + + + Enable parsers to parse and serialize custom resources that contain custom datatypes. + An example has been added which shows how to do this + here]]> + + + JSON parser was incorrectly encoding resource language attribute in JSON as an + array instead of a string. Thanks to David Hay for reporting! + + + Sébastien Rivière contributed an excellent pull request which adds a + number of enhancements to JAX-RS module: + +
  • Enable the conditional update and delete
  • +
  • Creation of a bundle provider, and support of the @Transaction
  • +
  • Bug fix on the exceptions handling as some exceptions throw outside bean context were not intercept.
  • +
  • Add the possibility to have the stacktrace in the jaxrsException
  • + + ]]> +
    + + FhirTerser.cloneInto method failed to clone correctly if the source + had any extensions. Thanks to GitHub user @Virdulys for submitting and + providing a test case! + + + Update DSTU2 InstanceValidator to latest version from upstream + + + Web Testing UI was not able to correctly post an STU3 transaction + + + DateTime parser incorrectly parsed times where more than 3 digits of + precision were provided on the seconds after the decimal point + + + Improve error messages when the $validate operation is called but no resource + is actually supplied to validate + + + DSTU2+ servers no longer return the Category header, as this has been + removed from the FHIR specification (and tags are now available in the + resource body so the header was duplication/wasted bandwidth) + + + Create and Update operations in server did not + include ETag or Last-Modified headers even though + the spec says they should. Thanks to Jim Steel for + reporting! + + + Update STU3 client and server to use the new sort parameter style (param1,-param2,param). Thanks to GitHub user @euz1e4r for + reporting! + + + QuantityClientParam#withUnit(String) put the unit into the system part of the + parameter value + + + Fluent client searches with date parameters were not correctly using + new prefix style (e.g. gt) instead of old one (e.g. >) + + + Some built-in v3 code systems for STU3 resources were missing + certain codes, which caused false failures when validating + resources. Thanks to GitHub user @Xoude for reporting! + + + Some methods on DSTU2 model structures have JavaDocs that + incorrectly claim that the method will not return null when + in fact it can. Thanks to Rick Riemer for reporting! + + + ResponseHighlightingInterceptor has been modified based on consensus + on Zulip with Grahame that requests that have a parameter of + _format=json]]> or + _format=xml]]> will output raw FHIR content + instead of HTML highlighting the content as they previously did. + HTML content can now be forced via the (previously existing) + _format=html]]> or via the two newly added + values + _format=html/json]]> and + _format=html/xml]]>. Because of this + change, the custom + _raw=true]]> mode has been deprecated and + will be removed at some point. + + + Operation definitions (e.g. for $everything operation) in the generated + server conformance statement should not include the $ prefix in the operation + name or code. Thanks to Dion McMurtrie for reporting! + + + Server generated OperationDefinition resources did not validate + due to some missing elements (kind, status, etc.). + Thanks to + Michael Lawley for reporting! + + + Operations that are defined on multiple resource provider types with + the same name (e.g. "$everything") are now automatically exposed by the server + as separate OperationDefinition resources per resource type. Thanks to + Michael Lawley for reporting! + + + OperationDefinition resources generated automatically by the server for operations + that are defined within resource/plain providers incorrectly stated that + the maximum cardinality was "*" for non-collection types with no explicit + maximum stated, which is not the behaviour that the JavaDoc on the + annotation describes. Thanks to Michael Lawley + for reporting! + + + Server parameters annotated with + @Since]]> + or + @Count]]> + which are of a FHIR type such as IntegerDt or DateTimeType will + now be set to null if the client's URL does not + contain this parameter. Previously they would be populated + with an empty instance of the FHIR type, which was inconsistent with + the way other server parameters worked. + + + Server now supports the _at parameter (including multiple repetitions) + for history operation + + + + AuthorizationInterceptor can now allow or deny requests to extended + operations (e.g. $everything) + + + DecimalType used BigDecimal constructor instead of valueOf method to + create a BigDecimal from a double, resulting in weird floating point + conversions. Thanks to Craig McClendon for reporting! + + + Remove the depdendency on a method from commons-lang3 3.3 which was + causing issues on some Android phones which come with an older version + of this library bundled. Thanks to Paolo Perliti for reporting! + + + Parser is now better able to handle encoding fields which have been + populated with a class that extends the expected class + + + When declaring a child with + order=Child.REPLACE_PARENT]]> + the serialized form still put the element at the + end of the resource instead of in the correct + order + + + Fix STU3 JPA resource providers to allow validate operation + at instance level + +
    + + + Security Fix: XML parser was vulnerable to XXE (XML External Entity) + processing, which could result in local files on disk being disclosed. + See this page]]> + for more information. + Thanks to Jim Steel for reporting! + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Hibernate (JPA, Web Tester): 5.0.7 -> 5.1.0
  • +
  • Spring (JPA, Web Tester): 4.2.4 -> 4.2.5
  • +
  • SLF4j (All): 1.7.14 -> 1.7.21
  • + + ]]> +
    + + Support comments when parsing and encoding both JSON and XML. Comments are retrieved + and added to the newly created methods + IBase#getFormatCommentsPre() and + IBase#getFormatCommentsPost() + + + Added options to the CLI upload-examples command which allow it to cache + the downloaded content file, or use an arbitrary one. Thanks to Adam Carbone + for the pull request! + + + REST search parameters with a prefix/comparator had not been updated to use + the DSTU2 style prefixes (gt2011-01-10) instead of the DSTU1 style prefixes + (>2011-01-01). The client has been updated so that it uses the new prefixes + if the client has a DSTU2+ context. The server has been updated so that it now + supports both styles. +
    ]]> + As a part of this change, a new enum called + ParamPrefixEnum]]> + has been introduced. This enum replaces the old + QuantityCompararatorEnum]]> + which has a typo in its name and can not represent several new prefixes added since + DSTU1. +
    + + JPA server number and quantity search params now follow the rules for the + use of precision in search terms outlined in the + search page]]> of the + FHIR specification. For example, previously a 1% tolerance was applied for + all searches (10% for approximate search). Now, a tolerance which respects the + precision of the search term is used (but still 10% for approximate search). + + + Fix a failure starting the REST server if a method returns an untyped List, which + among other things prevented resource provider added to the server + as CDI beans in a JBoss enviroment. Thanks to GitHub user fw060 (Fei) for + reporting and figuring out exactly why this wasn't working! + + + JPA server now supports :above and :below qualifiers on URI search params + + + Add optional support (disabled by default for now) to JPA server to support + inline references containing search URLs. These URLs will be resolved when + a resource is being created/updated and replaced with the single matching + resource. This is being used as a part of the May 2016 Connectathon for + a testing scenario. + + + The server no longer adds a + WWW-Authenticate]]> + header to the response if any resource provider code throws an + AuthenticationException]]>. This header is + used for interactive authentication, which isn't generally + appropriate for FHIR. We added code to add this header a long time + ago for testing purposes and it never got removed. Please let us + know if you need the ability to add this header automatically. Thanks + to Lars Kristian Roland for pointing this out. + + + In the client, the create/update operations on a Binary resource + (which use the raw binary's content type as opposed to the FHIR + content type) were not including any request headers (Content-Type, + User-Agent, etc.) Thanks to Peter Van Houte of Agfa Healthcare for + reporting! + + + Handling of Binary resources containing embedded FHIR resources for + create/update/etc operations has been corrected per the FHIR rules + outlined at + Binary Resource]]> + in both the client and server. +
    ]]> + Essentially, if the Binary contains something + that isn't FHIR (e.g. an image with an image content-type) the + client will send the raw data with the image content type to the server. The + server will place the content type and raw data into a Binary resource instance + and pass those to the resource provider. This part was already correct previous + to 1.5. +
    ]]> + On the other hand, if the Binary contains a FHIR content type, the Binary + is now sent by the client to the server as a Binary resource with a FHIR content-type, + and the embedded FHIR content is contained in the appropriate fields. The server + will pass this "outer" Binary resource to the resource provider code. +
    + + The RequestDetails and ActionRequestDetails objects which are passed to + server interceptor methods and may also be used as server provider method + arguments now has a new method + Map<String, String> getUserData() + ]]> + which can be used to pass data and objects between interceptor methods to + to providers. This can be useful, for instance, if an authorization + interceptor wants to pass the logged in user's details to other parts + of the server. + + + IServerInterceptor#incomingRequestPreHandled() is called + for a @Validate method, the resource was not populated in the + ActionRequestDetails argument. Thanks to Ravi Kuchi for reporting! + ]]> + + + [baseUrl]/metadata with an HTTP method + other than GET (e.g. POST, PUT) should result in an HTTP 405. Thanks to + Michael Lawley for reporting! + ]]> + + + Fix a server exception when trying to automatically add the profile tag + to a resource which already has one or more profiles set. Thanks to + Magnus Vinther for reporting! + + + QuantityParam parameters being used in the RESTful server were ignoring + the + :missing]]> + qualifier. Thanks to Alexander Takacs for reporting! + + + Annotation client failed with an exception if the response contained + extensions on fields in the resonse Bundle (e.g. Bundle.entry.search). + Thanks to GitHub user am202 for reporting! + + + Primitive elements with no value but an extension were sometimes not + encoded correctly in XML, and sometimes not parsed correctly in JSON. + Thanks to Bill de Beaubien for reporting! + + + The Web Testing UI has long had an issue where if you click on a button which + navigates to a new page (e.g. search, read, etc) and then click the back button + to return to the original page, the button you clicked remains disabled and can't + be clicked again (on Firefox and Safari). This is now fixed. Unfortunately the fix means that the + buttom will no longer show a "loading" spinner, but there doesn't seem to + be another way of fixing this. Thanks to Mark Scrimshire for reporting! + + + Extensions found while parsing an object that doesn't support extensions are now + reported using the IParserErrorHandler framework in the same way that + other similar errors are handled. This allows the parser to be more lenient + when needed. + + + Improve error message if incorrect type is placed in a list field in the data model. Java + uses generics to prevent this at compile time, but if someone is in an environment without + generics this helps improve the error message at runtime. Thanks to Hugo Soares for + suggesting. + + + Prevent an unneeded warning when parsing a resource containing + a declared extension. Thanks to Matt Blanchette for reporting! + + + Web Tester UI did not invoke VRead even if a version ID was specified. Thanks + to Poseidon for reporting! + + + Per discussion on the FHIR implementer chat, the JPA server no + longer includes _revinclude matches in the Bundle.total count, or the + page size limit. + + + JPA server now persists search results to the database in a new table where they + can be temporaily preserved. This makes the JPA server much more scalable, since it + no longer needs to store large lists of pages in memory between search invocations. +
    ]]> + Old searches are deleted after an hour by default, but this can be changed + via a setting in the DaoConfig. +
    + + JPA servers' resource version history mechanism + has been adjusted so that the history table + keeps a record of all versions including the + current version. This has the very helpful + side effect that history no longer needs to be + paged into memory as a complete set. Previously + history had a hard limit of only being able to + page the most recent 20000 entries. Now it has + no limit. + + + JPA server returned the wrong Bundle.type value (COLLECTION, should be SEARCHSET) + for $everything operation responses. Thanks to Sonali Somase for reporting! + + + REST and JPA server should reject update requests where the resource body does not + contain an ID, or contains an ID which does not match the URL. Previously these + were accepted (the URL ID was trusted) which is incorrect according to the + FHIR specification. Thanks to GitHub user ametke for reporting! +
    ]]> + As a part of this change, server error messages were also improved for + requests where the URL does not contain an ID but needs to (e.g. for + an update) or contains an ID but shouldn't (e.g. for a create) +
    + + When fields of type BoundCodeDt (e.g. Patient.gender) + are serialized and deserialized using Java's native + object serialization, the enum binder was not + serialized too. This meant that values for the + field in the deserialized object could not be + modified. Thanks to Thomas Andersen for reporting! + + + REST Server responded to HTTP OPTIONS requests with + any URI as being a request for the server's + Conformance statement. This is incorrect, as only + a request for OPTIONS [base url]]]> should be treated as such. Thanks to Michael Lawley for reporting! + + + REST annotation style client was not able to handle extended operations + ($foo) where the response from the server was a raw resource instead + of a Parameters resource. Thanks to Andrew Michael Martin for reporting! + + + JPA server applies _lastUpdated filter inline with other searches wherever possible + instead of applying this filter as a second query against the results of the + first query. This should improve performance when searching against large + datasets. + + + Parsers have new method + setDontEncodeElements]]> + which can be used to force the parser to not encode certain elements + in a resource when serializing. For example this can be used to omit + sensitive data or skip the resource metadata. + + + JPA server database design has been adjusted + so that different tables use different sequences + to generate their indexes, resulting in more sequential + resource IDs being assigned by the server + + + Server now correctly serves up Binary resources + using their native content type (instead of as a + FHIR resource) if the request contains an accept + header containing "application/xml" as some browsers + do. + + + DSTU2 resources now have a + getMeta()]]> method which returns a + modifiable view of the resource metadata for convenience. This + matches the equivalent method in the DSTU3 structures. + + + Add a new method to FhirContext called + setDefaultTypeForProfile + ]]> + which can be used to specify that when recources are received which declare + support for specific profiles, a specific custom structures should be used + instead of the default. For example, if you have created a custom Observation + class for a specific profile, you could use this method to cause your custom + type to be used by the parser for resources in a search bundle you receive. +
    + See the documentation page on + Profiles and Extensions + for more information. + ]]> +
    + + Parsing/Encoding a custom resource type which extends a + base type sometimes caused the FhirContext to treat all future + parses of the same resource as using the custom type even when + this was not wanted. +
    ]]> + Custom structures may now be explicitly declared by profile + using the + setDefaultTypeForProfile + ]]> + method. +
    ]]> + This issue was discovered and fixed as a part of the implementation of issue #315. +
    + + Set up the tinder plugin to work as an ant task + as well as a Maven plugin, and to use external + sources. Thanks to Bill Denton for the pull + request! + + + JPA server now allows searching by token + parameter using a system only and no code, + giving a search for any tokens which match + the given token with any code. Previously the + expected behaviour for this search + was not clear in the spec and HAPI had different + behaviour from the other reference servers. + + + Introduce a JAX-RS client provider which can be used instead of the + default Apache HTTP Client provider to provide low level HTTP + services to HAPI's REST client. See + JAX-RS & Alternate HTTP Client Providers]]> + for more information. +
    ]]> + This is useful in cases where you have other non-FHIR REST clients + using a JAX-RS provider and want to take advantage of the + rest of the framework. +
    ]]> + Thanks to Peter Van Houte from Agfa for the amazing work! +
    + + Parser failed with a NPE while encoding resources if the + resource contained a null extension. Thanks to + steve1medix for reporting! + + + In generated model classes (DSTU1/2) don't + use BoundCodeDt and BoundCodeableConceptDt for + coded fields which use example bindings. Thanks + to GitHub user Ricq for reporting! + + + @Operation will now infer the maximum number of repetitions + of their parameters by the type of the parameter. Previously if + a default max() value was not specified in the + @OperationParam annotation on a parameter, the maximum + was assumed to be 1. Now, if a max value is not explicitly specified + and the type of the parameter is a basic type (e.g. StringDt) the + max will be 1. If the parameter is a collection type (e.g. List<StringDt>) + the max will be * + ]]> + + + @Operation + may now use search parameter types, such as + TokenParam and + TokenAndListParam as values. Thanks to + Christian Ohr for reporting! + ]]> + + + Add databases indexes to JPA module search index tables + for the RES_ID column on each. This should help + performance when searching over large datasets. + Thanks to Emmanuel Duviviers for the suggestion! + + + DateTimeType should fail to parse 1974-12-25+10:00 as this is not + a valid time in FHIR. Thanks to Grahame Grieve for reporting! + + + When parsing a Bundle resource, if the Bundle.entry.request.url contains a UUID + but the resource body has no ID, the Resource.id will be populated with the ID from the + Bundle.entry.request.url. This is helpful when round tripping Bundles containing + UUIDs. + + + When parsing a DSTU3 bundle, references between resources did not have + the actual resource instance populated into the reference if the + IDs matched as they did in DSTU1/2. + + + Contained resource references on DSTU3 + resources were not serialized correctly when + using the Json Parser. Thanks to GitHub user + @fw060 for reporting and supplying a patch + which corrects the issue! + + + DSTU3 model classes equalsShallow and equalsDeep both did not work + correctly if a field was null in one object, but contained an empty + object in the other (e.g. a StringType with no actual value in it). These + two should be considered equal, since they would produce the exact same + wire format.
    ]]> + Thanks to GitHub user @ipropper for reporting and providing + a test case! +
    + + JPA server now supports searching for _tag:not=[tag]]]> + which enables finding resources that to not have a given tag/profile/security tag. + Thanks to Lars Kristian Roland for the suggestion! + + + Extensions containing resource references did not get encoded correctly + some of the time. Thanks to Poseidon for reporting! + + + Parsers (both XML and JSON) encoded the first few elements of DSTU3 structures in the wrong order: + Extensions were placed before any other content, which is incorrect (several + elements come first: meta, text, etc.) + + + In server implementations, the Bundle.entry.fullUrl was not getting correctly + populated on Hl7OrgDstu2 servers. Thanks to Christian Ohr for reporting! + + + Ensure that element IDs within resources (i.e. IDs on elements other than the + resource itself) get serialized and parsed correctly. Previously, these didn't get + serialized in a bunch of circumstances. Thanks to Vadim Peretokin for reporting + and providing test cases! + + + Improve CLI error message if the tool can't bind to the requested port. Thanks + to Claude Nanjo for the suggestion! + + + Server param of _summary=text]]> did not + include mandatory elements in return as well as + the text element, even though the FHIR specification + required it. + + + Remove invalid resource type "Documentation" from DSTU2 + structures. + + + JPA server did not respect target types for search parameters. E.g. Appointment:patient has + a path of "Appointment.participant.actor" and a target type of "Patient". The search path + was being correctly handled, but the target type was being ignored. + + + RestfulServer now manually parses URL parameters instead of relying on the container's + parsed parameters. This is useful because many Java servlet containers (e.g. Tomcat, Glassfish) + default to ISO-8859-1 encoding for URLs insetad of the UTF-8 encoding specified by + FHIR. + + + ResponseHighlightingInterceptor now doesn't highlight if the request + has an Origin header, since this probably denotes an AJAX request. + +
    + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Hibernate (JPA, Web Tester): 5.0.3 -> 5.0.7
  • +
  • Springframework (JPA, Web Tester): 4.2.2 -> 4.2.4
  • +
  • Phloc-Commons (Schematron Validator): 4.3.6 -> 4.4.4
  • +
  • Apache httpclient (Client): 4.4 -> 4.5.1
  • +
  • Apache httpcore (Client): 4.4 -> 4.4.4
  • +
  • SLF4j (All): 1.7.13 -> 1.7.14
  • + + ]]> +
    + + Remove a dependency on a Java 1.7 class + (ReflectiveOperationException) in several spots in the + codebase. This dependency was accidentally introduced in + 1.3, and animal-sniffer-plugin failed to detect it (sigh). + + + Add two new server interceptors: + RequestValidatingInterceptor + and + ResponseValidatingInterceptor + ]]> + which can be used to validate incoming requests or outgoing responses using the standard FHIR validation + tools. See the + Server Validation Page + ]]> + for examples of how to use these interceptors. These intereptors have both + been enabled on the + public test page. + ]]> + + + Make IBoundCodeableConcept and IValueSetEnumBinder serializable, + fixing an issue when trying to serialize model classes containing + bound codes. Thanks to Nick Peterson for the Pull Request! + + + Introduce a JAX-RS version of the REST server, which can be used + to deploy the same resource provider implementations which work + on the existing REST server into a JAX-RS (e.g. Jersey) environment. + Thanks to Peter Van Houte from Agfa for the amazing work! + + + CLI now supports writing to file:// URL for 'upload-examples' command + + + GZipped content is now supported for client-to-server uploads (create, update, transaction, etc.). + The server will not automatically detect compressed incoming content and decompress it (this can be + disabled using a RestfulServer configuration setting). A new client interceptor has been added + which compresses outgoing content from the client. + + + JPA server transaction attempted to validate resources twice each, + with one of these times being before anything had been committed to the + database. This meant that if a transaction contained both a Questionnaire + and a QuestionnaireResponse, it would fail because the QuestionnaireResponse + validator wouldn't be able to find the questionnaire. This is now corrected. + + + Add a new method to the generic/fluent client for searching: + .count(int)
    ]]> + This replaces the existing ".limitTo(int)" method which has + now been deprocated because it was badly named and undocumented. +
    + + Profile validator has been configured to allow extensions even if they + aren't explicitly declared in the profile. + + + Add a constraint that the Maven build will only run in JDK 8+. HAPI + remains committed to supporting JDK 6+ in the compiled library, but these + days it can only be built using JDK 8. Thanks to joelsch for the PR! + + + When serializing a value[x] field, if the value type was a profiled type (e.g. markdown is a + profile of string) HAPI 1.3 would use the base type in the element name, e.g. + valueString instead of valueMarkdown. After discussion with Grahame, this appears to + be incorrect behaviour so it has been fixed. + + + Support target parameter type in _include / _revinclude values, e.g. + _include=Patient:careProvider:Organization. Thanks to Joe Portner + for reporting! + + + Use ResponseHighlighterInterceptor in the hapi-fhir-jpaserver-example + project to provide nice syntax highlighting. Thanks to Rob Hausam for + noting that this wasn't there. + + + Introduce custom @CoverageIgnore annotation to hapi-fhir-base in order to + remove dependency on cobertura during build and in runtime. + + + Server-generated conformance statements incorrectly used /Profile/ instead + of /StructureDefinition/ in URL links to structures. + + + JsonParser has been changed so that when serializing numbers it will use + plain format (0.001) instead of scientific format (1e-3). The latter is + valid JSON, and the parser will still correctly parse either format (all + clients should be prepared to) but this change makes serialized + resources appear more consistent between XML and JSON. As a result of this + change, trailing zeros will now be preserved when serializing as well. + + + Add DSTU3 example to hapi-fhir-jpaserver-example. Thanks to Karl + Davis for the Pull Request! + + + RestfulServer#setUseBrowserFriendlyContentTypes has been deprecated and its + functionality removed. The intention of this feature was that if it + detected a request coming in from a browser, it would serve up JSON/XML + using content types that caused the browsers to pretty print. But + each browser has different rules for when to pretty print, and + after we wrote that feature both Chrome and FF changed their rules to break it anyhow. + ResponseHighlightingInterceptor provides a better implementation of + this functionality and should be used instead. + + + Narrative generator framework has removed the + ability to generate resource titles. This + functionality was only useful for DSTU1 + implementations and wasn't compatible + with coming changes to that API. + + + Remove dependency on Servlet-API 3.0+ by using methods available in 2.5 where possible. + Note that we continue to use Servlet-API 3.0+ features in some parts of the JPA API, so + running in an old serlvet container should be tested well before use. Thanks to Bill Denton + for reporting! + + + Add new methods to RestfulClientFactory allowing you to configure the size of the + client pool used by Apache HttpClient. Thanks to Matt Blanchette for the pull + request! + + + Add support for new modifier types on Token search params in Server and + annotation client. + + + Server conformance statement should include search parameter chains if the + chains are explicitly defined via @Search(whitelist={....}). Thanks to lcamilo15 + for reporting! + + + Remove afterPropertiesSet() call in Java config for JPA + server's EntityManagerFactory. This doesn't need to be called + manually, the the manual call led to a warning about + the EntityManager being created twice. + + + Allow server to correctly figure out it's own address even if the container provides + a Servlet Context Path which does not include the root. Thanks to Petro Mykhaylyshyn + for the pull request! + +
    + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Commons-lang3 (Core): 3.3.2 -> 3.4
  • +
  • Logback (Core): 1.1.2 -> 1.1.3
  • +
  • SLF4j (Core): 1.7.102 -> 1.7.12
  • +
  • Springframework (JPA, Web Tester): 4.1.5 -> 4.2.2
  • +
  • Hibernate (JPA, Web Tester): 4.2.17 -> 5."
  • +
  • Hibernate Validator (JPA, Web Tester): 5.2.1 -> 5.2.2
  • +
  • Derby (JPA, CLI, Public Server): 10.11.1.1 -> 10.12.1.1
  • +
  • Jetty (JPA, CLI, Public Server): 9.2.6.v20141205 -> 9.3.4.v20151007
  • + + ]]> +
    + + JPA and Tester Overlay now use Spring Java config files instead + of the older XML config files. All example projects have been updated. + + + JPA server removes duplicate resource index entries before storing them + (e.g. if a patient has the same name twice, only one index entry is created + for that name) + + + JPA server did not correctly index search parameters of type "reference" where the + path had multiple entries (i.e. "Resource.path1 | Resource.path2") + + + JPA server _history operations (server, type, instance) not correctly set the + Bundle.entry.request.method to POST or PUT for create and updates of the resource. + + + Support AND/OR on _id search parameter in JPA + + + Constructor for DateRanfeParam which dates in two DateParam instances was ignoring + comparators on the DateParam. + + + In JSON parsing, finding an object where an array was expected led to an unhelpful + error message. Thanks to Avinash Shanbhag for reporting! + + + JPA server gave an unhelpful error message if $meta-add or $meta-delete were called + with no meta elements in the input Parameters + + + Narrative generator did not include OperationOutcome.issue.diagnostics in the + generated narrative. + + + Clients (generic and annotation) did not populate the Accept header on outgoing + requests. This is now populated to indicate that the client supports both XML and + JSON unless the user has explicitly requested one or the other (in which case the + appropriate type only will be send in the accept header). Thanks to + Avinash Shanbhag for reporting! + + + QuestionnaireResponse validator now allows responses to questions of + type OPENCHOICE to be of type 'string' + + + JPA server should reject resources with a reference that points to an incorrectly typed + resource (e.g. points to Patient/123 but resource 123 is actually an Observation) or points + to a resource that is not valid in the location it is found in (e.g. points to Patient/123 but + the field supposed to reference an Organization). Thanks to Bill de Beaubien for reporting! + + + In server, if a client request is received and it has an Accept header indicating + that it supports both XML and JSON with equal weight, the server's default is used instead of the first entry in the list. + + + JPA server now supports searching with sort by token, quantity, + number, Uri, and _lastUpdated (previously only string, date, and _id + were supported) + + + Fix issue in JPA where a search with a _lastUpdated filter which matches no results + would crash if the search also had a _sort + + + Fix several cases where invalid requests would cause an HTTP 500 instead of + a more appropriate 400/404 in the JPA server (vread on invalid version, + delete with no ID, etc.) + + + Fix narrative generation for DSTU2 Medication resource + + + Profile validator now works for valuesets which use + v2 tables + + + JPA server Patient/[id]/$everything operation now supports + _lastUpdated filtering and _sort'ing of results. + + + Fix parser issue where profiled choice element datatypes (e.g. value[x] where one allowable + type is Duration, which is a profile of Quantity) get incorrectly encoded using the + profiled datatype name instead of the base datatype name as required by the FHIR + spec. Thanks to Nehashri Puttu Lokesh for reporting! + + + Some generated Enum types in DSTU2 HAPI structures + did not have latest valueset definitions applied. Thanks + to Bill de Beaubien for reporting! + + + JPA server can now successfully search for tokens pointing at code values + (values with no explicit system but an implied one, such as Patient.gender) + even if the system is supplied in the query. + + + Correct issues with Android library. Thanks to + Thomas Andersen for the submission! + + + JPA server incorrectly rejected match URLs + if they did not contain a question mark. Thanks + to Bill de Beaubien for reporting! + + + Remove invalid entries in OSGi Manifest. Thanks + to Alexander Kley for the fix! + + + JPA server now supports $everything on Patient and Encounter types (patient and encounter instance was already supported) + + + Generic client operation invocations now + have an additional inline method for generating the input + Parameters using chained method calls instead + of by passing a Parameters resource in + + + Parsing an XML resource where the XHTML + namespace was declared before the beginning + of the narrative section caused an invalid + re-encoding when encoding to JSON. + + + Conditional deletes in JPA did not correctly + process if the condition had a chain or a + qualifier, e.g. "Patient?organization.name" or + "Patient.identifier:missing" + + + Generic/fluent client search can now be + performed using a complete URL supplied + by user code. Thanks to Simone Heckmann + pointing out that this was needed! + + + Refactor JPA $everything operations so that + they perform better + + + Server operation methods can now declare the + ID optional, via + @IdParam(optional=true) + meaning that the same operation can also be invoked + at the type level. + + + Make JPA search queries with _lastUpdated parameter a bit more efficient + + + Clean up Android project to make it more lightweight and remove a + number of unneeded dependencies. Thanks to Thomas Andersen + for the pull request! + + + Fix a crash when encoding a Binary resource in JSON encoding + if the resource has no content-type + + + JPA server now supports read/history/search in transaction entries + by calling the actual implementing method in the server (previously + the call was simulated, which meant that many features did not work) + + + ResourceReferenceDt#loadResource(IRestfulClient) did not + use the client's read functionality, so it did not + handle JSON responses or use interceptors. Thanks to + JT for reporting! + + + JPA server maximumn length for a URI search parameter has been reduced from + 256 to 255 in order to accomodate MySQL's indexing requirements + + + Server failed to respond correctly to compartment search operations + if the same provider also contained a read operation. Thanks to GitHub user + @am202 for reporting! + + + Fix issue in testpage-overlay's new Java configuration where only the first + configured server actually gets used. + + + Introduce + IJpaServerInterceptor]]> + interceptors for JPA server which can be used for more fine grained operations. + + + Parser (XML and JSON) shouldn't encode an ID tag in resources + which are part of a bundle when the resource has a UUID/OID + ID. + + + Add ability for a server REST resource provider @Search method + to declare that it should allow even parameters it doesn't + understand. + + + Correctly set the Bundle.type value on all pages of a search result in + the server, and correcltly set the same value in JPA server $everything + results. + + + JPA $everything operations now support new parameters _content + and _text, which work the same way as the same parameters on a + search. This is experimental, since it is not a part of the core + FHIR specification. + + + Process "Accept: text/xml" and "Accept: text/json" headers was + wanting the equivalent FHIR encoding styles. These are not + correct, but the intention is clear so we will honour them + just to be helpful. + + + Generated Enum types for some ValueSets did not include all + codes (specifically, ValueSets which defined concepts containing + child concepts did not result in Enum values for the child concepts) + + + In the JPA server, order of transaction processing should be + DELETE, POST, PUT, GET, and the order should not matter + within entries with the same verb. Thanks to Bill de Beaubien + for reporting! + + + Add the ability to wire JPA conformance providers + using Spring (basically, add default constructors + and setters to the conformance providers). Thanks + to C. Mike Bylund for the pull request! + +
    + + + JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire + if one is declared. + + + SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions. + + + Model classes do not use BoundCodeableConcept for example bindings that do not + actually point to any codes (e.g. Observation.interpretation). Thanks + to GitHub user @steve1medix for reporting! + + + Server now exports operations as separate resources instead of as contained resources + within Conformance + + + Add new operation $get-resource-counts which will replace the resource + count extensions exported in the Conformance statement by the JPA + server. + + + JPA server sorting often returned unexpected orders when multiple + indexes of the same type were found on the same resource (e.g. multiple string indexed fields). Thanks to Travis Cummings for reporting! + + + Add another method to IServerInterceptor which converts an exception generated on the server + into a BaseServerResponseException. This is useful so that servers using ResponseHighlighterInterceptor + will highlight exceptions even if they aren't created with an OperationOutcome. + + + XmlParser and JsonParser in DSTU2 mode should not encode empty + tags in resource. Thanks to Bill De Beaubien for reporting! + + + OperationDefinitions generated by server did not properly document + their return parameters or the type of their input parameters. + + + Operations in server generated conformance statement should only + appear once per name, since the name needs to be unique. + + + Resources and datatypes are now serializable. This is an + experimental feature which hasn't yet been extensively tested. Please test and give us your feedback! + + + Switch REST server to using HttpServletRequest#getContextPath() to get + the servlet's context path. This means that the server should behave more + predictably, and should work in servlet 2.4 environments. Thanks to + Ken Zeisset for the suggestion! + + + Vagrant environment now has an apt recipt to ensure that + package lists are up to date. Thanks to GitHub user + Brian S. Corbin (@corbinbs) for thr contribution! + + + JPA server and generic client now both support the _tag search parameter + + + Add support for BATCH mode to JPA server transaction operation + + + Server was not correctly unescaping URL parameter values with + a trailing comma or an escaped backslash. Thanks to GitHub user + @SherryH for all of her help in diagnosing this issue! + + + Avoid crash when parsing if an invalid child element is found in + a resource reference. + + + Create new android specialty libraries for DSTU1 and DSTU2 + + + Throwing a server exception (e.g. AuthenticationException) in a server interceptor's + incomingRequestPreProcessed method resulted in the server returning an HTTP 500 instead + of the appropriate error code for the exception being thrown. Thanks to Nagesh Bashyam + for reporting! + + + Fix issue in JSON parser where invalid contained resources (missing + a resourceType element) fail to parse with a confusing NullPointerException. + Thanks to GitHub user @hugosoares for reporting! + + + JPA server now implements the $validate-code operation + + + HAPI-FHIR now has support for _summary and _elements parameters, in server, client, + and JPA server. + + + _revinclude results from JPA server should have a Bundle.entry.search.mode of + "include" and not "match". Thanks to Josh Mandel for reporting! + + + Resource references using resource instance objects instead of resource IDs + will correctly qualify the IDs with the resource type if they aren't already qualified + + + Testpage Overlay project now properly allows a custom client + factory to be used (e.g. for custom authentication, etc.) Thanks + to Chin Huang (@pukkaone) for the pull request! + + + JPA server should reject IDs containing invalid characters (e.g. "abc:123") + but should allow client assigned IDs that contain text but do not start with + text. Thanks to Josh Mandel for reporting! + + + :text modifier on server and JPA server did not work correctly. Thanks to + Josh Mandel for reporting! + + + Fix issue in client where parameter values containing a comma were + sometimes double escaped. + + + _include parameters now support the new _include:recurse=FOO]]> + syntax that has been introduced in DSTU2 in the Client, Server, and JPA Server modules. + Non-recursive behaviour is now the default (previously it was recursive) and :recurse + needs to be explicitly stated in order to support recursion. + + + New operations added to JPA server to force re-indexing of all + resources (really only useful after indexes change or bugs are + fixed) + + + JPA server did not correctly index search parameters + of type "URI". Thanks to David Hay for reporting! Note that if you are using the JPA server, this change means that + there are two new tables added to the database schema. Updating existing resources in the database may fail unless you + set default values for the resource + table by issuing a SQL command similar to the following (false may be 0 or something else, depending on the database platform in use) + update hfj_resource set sp_coords_present = false;
    + update hfj_resource set sp_uri_present = false;
    ]]> +
    + + FIx issue in JPA server where profile declarations, tags, and + security labels were not always properly removed by an update that + was trying to remove them. Also don't store duplicates. + + + Instance $meta operations on JPA server did not previously return the + resource version and lastUpdated time + + + Server responses populate Bundle.entry.fullUrl if possible. Thanks + to Bill de Beaubien for reporting! + + + XML parser failed to initialize in environments where a very old Woodstox + library is in use (earlier than 4.0). Thanks to Bill de Beaubien for + reporting! + + + Invalid/unexpected attributes found when parsing composite elements + should be logged or reported to the parser error handler + + + JPA server can now store Conformance resources, per a request + from David Hay + + + ResponseHighlightingInterceptor now skips handling responses if it + finds a URL parameter of _raw=true]]> (in other + words, if this parameter is found, the response won't be returned as + HTML even if the request is detected as coming from a browser. + + + RestfulServer now supports dynamically adding and removing resource providers + at runtime. Thanks to Bill Denton for adding this. + + + JPA server now correctly suppresses contents of deleted resources + in history + + + JPA server returned deleted resources in search results when using the _tag, _id, _profile, or _security search parameters + + + Fix issue with build on Windows. Thanks to Bryce van Dyk for the pull request! + + + JPA server now supports $validate operation completely, including delete mode + and profile validation using the RI InstanceValidator + +
    + + + Add support for reference implementation structures. + + + Parsers did not encode the resource meta element if the resource + had tags but no other meta elements. Thanks to Bill de Beaubien and + Claude Nanjo for finding this. + + + Correct performance issue with :missing=true search requests where the parameter is a resource link. Thanks to wanghaisheng for all his help in testing this. + + + The self link in the Bundle returned by searches on the server does not respect the + server's address strategy (which resulted in an internal IP being shown on fhirtest.uhn.ca) + + + Introduce ResponseHighlighterInterceptor, which provides syntax highlighting on RESTful server responses + if the server detects that the request is coming from a browser. This interceptor has been added + to fhirtest.uhn.ca responses. + + + Performing a create operation in a client used an incorrect URL if the + resource had an ID set. ID should be ignored for creates. Thanks to + Peter Girard for reporting! + + + Add better addXXX() methods to structures, which take the datatype being added as a parameter. Thanks to Claude Nanjo for the + suggestion! + + + Add a new parser validation mechanism (see the + validation page]]> for info) which can be + used to validate resources as they are being parsed, and optionally fail if invalid/unexpected + elements are found in resource bodies during parsing. + + + IParser#parseResource(Class, String) method, which is used to parse a resource into the given + structure will now throw a DataFormatException if the structure is for the wrong type of + resource for the one actually found in the input String (or Reader). For example, if a Patient + resource is being parsed into Organization.class this will now cause an error. Previously, + the XML parser would ignore the type and the JSON parser would fail. This also caused + operations to not parse correctly if they returned a resource type other than + parameters with JSON encoding (e.g. the $everything operation on UHN's test server). + Thanks to Avinash Shanbhag for reporting! + + + Web tester UI now supports _revinclude + + + Support link elements in Bundle.entry when parsing in DSTU2 mode + using the old (non-resource) Bundle class. Thanks to GitHub user + @joedai for reporting! + + + LoggingInterceptor for server now supports logging DSTU2 extended operations by name + + + Woodstox XML parser has a default setting to limit the maximum + length of an attribute to 512kb. This caused issues handling + large attachments, so this setting has been increased to 100Mb. + Thanks to Nikos Kyriakoulakos for reporting! + + + Some HTML entities were not correctly converted during parsing. Thanks to + Nick Kitto for reporting! + + + In the JPA Server: + Transactions creating resources with temporary/placeholder resource IDs + and other resources with references to those placeholder IDs previously + did not work if the reference did not contain the resource type + (e.g. Patient/urn:oid:0.1.2.3 instead of urn:oid:0.1.2.3). The + latter is actually the correct way of specifying a reference to a + placeholder, but the former was the only way that worked. Both forms + now work, in order to be lenient. Thanks to Bill De Beaubien for + reporting! + + + When parsing Bundles, if Bundle.entry.base is set to "cid:" (for DSTU1) + or "urn:uuid:" / "urn:oid:" (for DSTU2) this is now correctly passed as + the base in resource.getId(). Conversely, when + encoding bundles, if a resource ID has a base defined, + and Bundle.entry.base is empty, it will now be + automatically set by the parser. + + + Add fluent client method for validate operation, and support the + new DSTU2 style extended operation for $validate if the client is + in DSTU2 mode. Thanks to Eric from the FHIR Skype Implementers chat for + reporting. + + + Server now supports complete Accept header content negotiation, including + q values specifying order of preference. Previously the q value was ignored. + + + Server in DSTU2 mode now indicates that whether it has support for Transaction operation or not. Thanks to Kevin Paschke for pointing out that this wasn't working! + + + Questionnaire.title now gets correctly indexed in JPA server (it has no path, so it is a special case) + + + JPA server now supports ifNoneMatch in GET within a transaction request. + + + DateRangeParam now supports null values in the constructor for lower or upper bounds (but + still not both) + + + Generic/fluent client and JPA server now both support _lastUpdated search parameter + which was added in DSTU2 + + + JPA server now supports sorting on reference parameters. Thanks to + Vishal Kachroo for reporting that this wasn't working! + + + Prevent Last-Updated header in responses coming back to the client from + overwriting the 'lastUpdated' value in the meta element in DSTU2 + resources. This is important because 'lastUpdated' can have more + precision than the equivalent header, but the client previously + gave the header priority. + + + JPA server supports _count parameter in transaction containing search URL (nested search) + + + DSTU2 servers now indicate support for conditional create/update/delete in their + conformance statement. + + + Support for the Prefer header has been added to the server, client, and + JPA modules. + + + JPA server failed to search for deep chained parameters across multiple references, + e.g. "Location.partof.partof.organization". Thanks to Ismael Sarmento Jr for + reporting! + + + Prevent crash when encoding resources with contained resources + if the contained resources contained a circular reference to each other + + + Add $meta, $meta-add, and $meta-delete operations to generic client + + + + + Bump the version of a few dependencies to the + latest versions: + +
  • Phloc-commons (for schematron validation) 4.3.5 -> 4.3.6
  • +
  • Apache HttpClient 4.3.6 -> 4.4
  • +
  • Woodstox 4.4.0 -> 4.4.1
  • +
  • SLF4j 1.7.9 -> 1.7.10
  • +
  • Spring (used in hapi-fhir-jpaserver-base module) 4.1.3.RELEASE -> 4.1.5.RELEASE
  • + + ]]> +
    + + Add support for "profile" and "tag" elements in the resource Meta block + when parsing DSTU2 structures. + + + When a user manually creates the list of contained resources in a resource, + the encoder fails to encode any resources that don't have a '#' at the + start of their ID. This is unintuitive, so we now assume that '123' means '#123'. + Thanks to myungchoi for reporting and providing a test case! + + + Add methods for setting the default encoding (XML/JSON) and + oretty print behaviour in the Fluent Client. Thanks to Stackoverflow + user ewall for the idea. + + + JPA Server did not mark a resource as "no longer deleted" if it + was updated after being deleted. Thanks to Elliott Lavy and Lloyd + McKenzie for reporting! + + + Fix regression in 0.9 - Server responds with an HTTP 500 and a NullPointerException instead of an HTTP 400 and a useful error message if the client requests an unknown resource type + + + Add support for + _revinclude]]> + parameter in client, server, and JPA. + + + Include constants on resources (such as + Observation.INCLUDE_VALUE_STRING]]>) + have been switched in the DSTU2 structures to use + the new syntax required in DSTU2: [resource name]:[search param NAME] + insead of the DSTU1 style [resource name].[search param PATH] + + + When encoding resources, the parser will now convert any resource + references to versionless references automatically (i.e. it will + omit the version part automatically if one is present in the reference) + since references between resources must be versionless. Additionally, + references in server responses will omit the server base URL part of the + reference if the base matches the base for the server giving + the response. + + + Narrative generator incorrectly sets the Resource.text.status to 'generated' even if the + given resource type does not have a template (and therefore no narrative is actually generated). + Thanks to Bill de Beaubien for reporting! + + + Searching in JPA server with no search parameter returns deleted resources when it should exclude them. + + + Remove Eclipse and IntelliJ artifacts (.project, *.iml, etc) from version control. Thanks + to Doug Martin for the suggestion! + + + REST server methods may now have a parameter of + type NarrativeModeEnum which will be populated with + the value of the _narrative URL parameter + if one was supplied. Annotation client methods + may also include a parameter of this type, and it + will be used to populate this parameter on the request + URL if it is not null. Thanks to Neal Acharya for the + idea! + + + Android JAR now includes servlet-API classes, as the project will not + work without them. Thanks + + + Requested _include values are preserved across paging links when the + server returns multiple pages. Thanks to Bill de Beaubien for + reporting! + + + Add new server address strategy "ApacheProxyAddressStrategy" which uses + headers "x-forwarded-host" and "x-forwarded-proto" to determine the + server's address. This is useful if you are deploying a HAPI FHIR + server behind an Apache proxy (e.g. for load balancing or other reasons). + Thanks to Bill de Beaubien for contributing! + + + Resource references between separate resources found in a single + bundle did not get populated with the actual resource when parsing a + DSTU2 style bundle. Thanks to Nick Peterson for reporting and figuring + out why none of our unit tests were actually catching the problem! + + + JSON encoder did not encode contained resources when encoding + a DSTU2 style bundle. Thanks to Mohammad Jafari and baopingle + for all of their help in tracking this issue down and developing + useful unit tests to demonstrate it. + + + Client now supports invoking transcation using a DSTU2-style + Bundle resource as the input. + + + JPA Server $everything operation could sometimes include a duplicate copy of + the main focus resource if it was referred to in a deep chain. Thanks + to David Hay for reporting! + + + JPA Server $everything operation now allows a _count parameter + + + JPA server failed to index resources containing ContactPointDt elements with + populated values (e.g. Patient.telecom). Thanks to Mohammad Jafari for reporting! + + + Add a new configuration method on the parsers, + setStripVersionsFromReferences(boolean)]]> which + configures the parser to preserve versions in resource reference links when + encoding. By default, these are removed. + + + Terser's IModelVisitor now supplies to the path to the element. This is + an API change, but I don't think there are many users of the IModelVisitor yet. + Please let us know if this is a big hardship and we can find an alternate way + of making this change. + + + Prevent server from returning a Content-Location header for search + response when using the DSTU2 bundle format + + + JPA server (uhnfhirtest.uhn.ca) sometimes included an empty + "text" element in Bundles being returned. + + + Add a framework for the Web Tester UI to allow its internal FHIR client to + be configured (e.g. to add an authorization interceptor so that it adds + credentials to client requests it makes). Thanks to Harsha Kumara for + the suggestion! + + + Fix regression in early 1.0 builds where resource type sometimes does not get + populated in a resource ID when the resource is parsed. Thanks to + Nick Peterson for reporting, and for providing a test case! + + + Allow fluent/generic client users to execute a transaction using a raw string (containing a bundle resource) + as input instead of a Bundle resource class instance. + + + Disable date validation in the web tester UI, so that it is possible to + enter partial dates, or dates without times, or even test out invalid date + options. + + + Make BaseElement#getUndeclaredExtensions() and BaseElement#getUndeclaredExtensions() return + a mutable list so that it is possible to delete extensions from a resource instance. + + + Server conformance statement check in clients (this is the check + where the first time a given FhirContext is used to access a given server + base URL, it will first check the server's Conformance statement to ensure + that it supports the correct version of FHIR) now uses any + registered client interceptors. In addition, IGenericClient now has a method + "forceConformanceCheck()" which manually triggers this check. Thanks to + Doug Martin for reporting and suggesting! + + + Rename the Spring Bean definition for the JPA server EntityManager from + "myEntityManagerFactory" to just "entityManagerFactory" as this is the + default bean name expected in other parts of the Spring framework. + Thanks to Mohammad Jafari for the suggestion! + + + Improve error message when a user tries to perform a create/update with an invalid + or missing Content-Type header. Thanks to wanghaisheng for reporting! (This was + actually a three part bug, so the following two fixes also reference this + bug number) + + + Add support for :missing qualifier in generic/fluent client. + + + Add support for :missing qualifier in JPA server. + + + Add a new configuration method on the parsers, + setStripVersionsFromReferences(boolean)]]> which + configures the parser to preserve versions in resource reference links when + encoding. By default, these are removed. + + + Add an exception for RESTful clients/servers to represent the + HTTP 403 Forbidden status code. Thanks to Joel Costigliola for + the patch! + + + Transaction server operations incorrectly used the "Accept" header instead of the "Content-Type" header to determine the + POST request encoding. Thanks to Rene Spronk for providing a test case! + +
    + + + Support for DSTU2 features introduced: New resource definitions, Bundle resource, + encoding changes (ID in resource bodt, meta tag) + + + Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object + would be an array even if the field it was extending was not repeatable. This is not correct + according to the specification, nor can HAPI's parser parse this correctly. The encoder + has been corrected, and the parser has been adjusted to be able to handle resources with + extensions encoded in this way. Thanks to Mohammad Jafari for reporting! + + + Library now checks if custom resource types can be instantiated on startup + (e.g. because they don't have a no-argument constructor) in order to + avoid failing later + + + Bump a few dependency JARs to the latest versions in Maven POM: + +
  • SLF4j (in base module) - Bumped to 1.7.9
  • +
  • Apache HTTPClient (in base module) - Bumped to 4.3.6
  • +
  • Hibernate (in JPA module) - Bumped to 4.3.7
  • + + ]]> +
    + + IdDt failed to recognize local identifiers containing fragments that look like + real identifiers as being local identifiers even though they started with '#'. + For example, a local resource reference of "#aa/_history/aa" would be incorrectly + parsed as a non-local reference. + Thanks to Mohammad Jafari for reporting! + + + Last-Modified]]> + header in server was incorrectly using FHIR date format instead + of RFC-1123 format. + + + Server create and update methods failed with an IllegalArgumentException if + the method type was a custom resource definition type (instead of a built-in + HAPI type). Thanks to Neal Acharya for the analysis. + + + JPA server module now supports + _include]]> + value of + *]]>. Thanks to Bill de Beaubien for reporting! + + + IdDt method + + returned String (unlike all of the other "withFoo" methods on that class), + and did not work correctly if the IdDt already had a server base. This + has been corrected. Note that the return type for this method has been + changed, so code may need to be updated. + + + In previous versions of HAPI, the XML parser encoded multiple contained + resources in a single + <contained></contained>]]> + tag, even though the FHIR specification rerquires a separate + <contained></contained>]]> + tag for each resource. This has been corrected. Note that the parser will + correctly parse either form (this has always been the case) so this + change should not cause any breakage in HAPI based trading partners, but + may cause issues if other applications have been coded to depend on the + incorrect behaviour. Thanks to Mochaholic for reporting! + + + Custom/user defined resource definitions which contained more than one + child with no order defined failed to initialize properly. Thanks to + Andy Huang for reporting and figuring out where the + problem was! + + + RESTful Client now queries the server (only once per server base URL) to ensure that + the given server corresponds to the correct version of the FHIR specification, as + defined by the FhirContext. This behaviour can be disabled by setting the + appropriate configuration on the + RestfulClientConfig. Thanks to Grahame Grieve for the suggestion! + + + JPA module now supports deleting resource via transaction + + + DateClientParam#second() incorrectly used DAY precision instead + of SECOND precision. Thanks to Tom Wilson for the pull request! + + + Fix issue where HAPI failed to initialize correctly if Woodstox library was not on the classpath, even + if StAX API was configured to use a different provider. Thanks to + James Butler for reporting and figuring out where the issue was! + + + Calling BaseDateTimeDt#setValue(Date, TemporalPrecisionEnum) did not always actually respect + the given precision when the value was encoded. Thanks to jacksonjesse for + reporting! + + + Encoders (both XML and JSON) will no longer encode contained resources if they are + not referenced anywhere in the resource via a local reference. This is just a convenience + for users who have parsed a resource with contained resources and want to remove some + before re-encoding. Thanks to Alexander Kley for reporting! + + + Add support for DSTU2 style security labels in the parser and encoder. Thanks to + Mohammad Jafari for the contribution! + + + Server requests for Binary resources where the client has explicitly requested XML or JSON responses + (either with a _format]]> URL parameter, or an Accept]]> request header) + will be responded to using the Binary FHIR resource type instead of as Binary blobs. This is + in accordance with the recommended behaviour in the FHIR specification. + + + Add new properties to RestfulServer: "DefaultResponseEncoding", which allows + users to configure a default encoding (XML/JSON) to use if none is specified in the + client request. Currently defaults to XML. Also "DefaultPrettyPrint", which specifies + whether to pretty print responses by default. Both properties can be overridden + on individual requets using the appropriate Accept header or request URL parameters. + + + Add support for quantity search params in FHIR tester UI + + + Add support for FHIR "extended operations" as defined in the FHIR DSTU2 + specification, for the Generic Client, Annotation Client, and + Server. + + + Observation.applies[x] and other similar search fields with multiple allowable + value types were not being correctly indexed in the JPA server. + + + DateClientParam.before() incorrectly placed "<=" instead of + "<" in the request URL. Thanks to Ryan for reporting! + + + Server now only automatically adds _include resources which are provided + as references if the client request actually requested that specific include. + See RestfulServer + + + User defined resource types which contain extensions that use a bound code type + (e.g. an BoundCodeDt with a custom Enum) failed to parse correctly. Thanks + to baopingle for reporting and providing a test case! + + + Sorting is now supported in the Web Testing UI (previously a button existed for sorting, but it didn't do anything) + + + Server will no longer include stack traces in the OperationOutcome returned to the client + when an exception is thrown. A new interceptor called ExceptionHandlingInterceptor has been + created which adds this functionality back if it is needed (e.g. for DEV setups). See the + server interceptor documentation for more information. Thanks to Andy Huang for the suggestion! + +
    + + + API CHANGE:]]> The "FHIR structures" for DSTU1 (the classes which model the + resources and composite datatypes) have been moved out of the core JAR into their + own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast + version is finalized. See the + DSTU2 page]]> + for more information. + + + Deprocated API Removal: The following classes (which were deprocated previously) + have now been removed: +
      +
    • ISecurityManager: If you are using this class, the same functionality + is available through the more general purpose + server interceptor + capabilities. +
    • CodingListParam: This class was made redundant by the + TokenOrListParam + class, which can be used in its place. +
    + ]]> +
    + + API Change: The IResource#getResourceMetadata() method has been changed + from returning + Map<ResourceMetadataKeyEnum<?>, Object> + to returning a new type called + ResourceMetadataMap. This new type implements + Map<ResourceMetadataKeyEnum<?>, Object> + itself, so this change should not break existing code, but may + require a clean build in order to run correctly. + ]]> + + + Profile generation on the server was not working due to IdDt being + incorrectly used. Thanks to Bill de Beaubien for the pull request! + + + Profiles did not generate correctly if a resource definition class had a + defined extension which was of a composite type. Thanks to Bill de Beaubien for the pull request! + + + Remove unnecessary IOException from narrative generator API. Thanks to + Petro Mykhailysyn for the pull request! + + + Introduced a new + @ProvidesResources]]> annotation which can be added to + resource provider and servers to allow them to declare additional resource + classes they are able to serve. This is useful if you have a server which can + serve up multiple classes for the same resource type (e.g. a server that sometimes + returns a default Patient, but sometimes uses a custom subclass). + Thanks to Bill de Beaubien for the pull request! + + + Introduced a new + @Destroy]]> annotation which can be added to + a resource provider method. This method will be called by the server when it + is being closed/destroyed (e.g. when the application is being undeployed, the + container is being shut down, etc.) + Thanks to Bill de Beaubien for the pull request! + + + Add a new method to the server interceptor + framework which allows interceptors to be notified of any exceptions and + runtime errors within server methods. Interceptors may optionally also + override the default error handling behaviour of the RestfulServer. + + + Add constants to BaseResource for the "_id" search parameter which all resources + should support. + + + DateRangeParam parameters on the server now return correct + getLowerBoundAsInstant()]]> + and + getUpperBoundAsInstant()]]> + values if a single unqualified value is passed in. For example, if + a query containing + &birthdate=2012-10-01]]> + is received, previously these two methods would both return the same + value, but with this fix + getUpperBoundAsInstant()]]> + now returns the instant at 23:59:59.9999. + + + Resource fields with a type of "*" (or Any) sometimes failed to parse if a + value type of "code" was used. Thanks to Bill de Beaubien for reporting! + + + Remove dependency on JAXB libraries, which were used to parse and encode + dates and times (even in the JSON parser). JAXB is built in to most JDKs + but the version bundled with IBM's JDK is flaky and resulted in a number + of problems when deploying to Websphere. + + + Primitive datatypes now preserve their original string value when parsing resources, + as well as containing the "parsed value". For instance, a DecimalDt field value of + 1.0000]]> will be parsed into the corresponding + decimal value, but will also retain the original value with the corresponding + level of precision. This allows vadliator rules to be applied to + original values as received "over the wire", such as well formatted but + invalid dates, e.g. "2001-15-01". Thanks to Joe Athman for reporting and + helping to come up with a fix! + + + When using Generic Client, if performing a + or operation using a String as the resource body, + the client will auto-detect the FHIR encoding style and send an appropriate + header. + + + JPA module (and public HAPI-FHIR test server) were unable to process resource types + where at least one search parameter has no path specified. These now correctly save + (although the server does not yet process these params, and it should). Thanks to + GitHub user shvoidlee for reporting and help with analysis! + + + Generic/Fluent Client "create" and "update" method requests were not setting a content type header + + + DateDt left precision value as in the constructor + . + + + RESTful server now doesn't overwrite resource IDs if they are absolute. In other words, if + a server's Resource Provider returns a resource with ID "Patient/123" it will be translated to + "[base url]/Patient/123" but if the RP returns ID "http://foo/Patient/123" the ID will be + returned exactly as is. Thanks to Bill de Beaubien for the suggestion! + + + JPA module Transaction operation was not correctly replacing logical IDs + beginning with "cid:" with server assigned IDs, as required by the + specification. + + + did not visit or find children in contained resources when + searching a resource. This caused server implementations to not always return contained + resources when they are included with a resource being returned. + + + Add a method which returns the name of the + resource in question (e.g. "Patient", or "Observation"). This is intended as a + convenience to users. + + + Do not strip version from resource references in resources returned + from server search methods. Thanks to Bill de Beaubien for reporting! + + + Correct an issue with the validator where changes to the underlying + OperationOutcome produced by a validation cycle cause the validation + results to be incorrect. + + + Client interceptors registered to an interface based client instance + were applied to other client instances for the same client interface as well. (Issue + did not affect generic/fluent clients) + + + DateDt, DateTimeDt and types InstantDt types now do not throw an exception + if they are used to parse a value with the wrong level of precision for + the given type but do throw an exception if the wrong level of precision + is passed into their constructors.
    ]]> + This means that HAPI FHIR can now successfully parse resources from external + sources that have the wrong level of precision, but will generate a validation + error if the resource is validated. Thanks to Alexander Kley for the suggestion! +
    + + Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks + to Alexander Kley for reporting! + + + Server gives a more helpful error message if multiple IResourceProvider implementations + are provided for the same resource type. Thanks to wanghaisheng for the idea! + + + Bring DSTU1 resource definitions up to version 0.0.82-2929]]> + Bring DEV resource definitions up to 0.4.0-3775]]> + Thanks to crinacimpian for reporting! + + + JPA server did not correctly process _include requests if included + resources were present with a non-numeric identifier. Thanks to + Bill de Beaubien for reporting! + + + Client requests which include a resource/bundle body (e.g. create, + update, transaction) were not including a charset in the content type + header, leading to servers incorrectly assuming ISO-8859/1. Thanks to + shvoidlee for reporting! + + + Clean up the way that Profile resources are automatically exported + by the server for custom resource profile classes. See the + @ResourceDef]]> + JavaDoc for information on how this works. + + + Add convenience methods to TokenOrListParam to test whether any of a set of tokens match + the given requested list. + + + Add a protected method to RestfulServer which allows developers to + implement their own method for determining which part of the request + URL is the FHIR request path (useful if you are embedding the RestulServer inside + of another web framework). Thanks to Harsha Kumara for the pull request! + +
    + + + API CHANGE:]]> The TagList class previously implemented ArrayList semantics, + but this has been replaced with LinkedHashMap semantics. This means that the list of + tags will no longer accept duplicate tags, but that tag order will still be + preserved. Thanks to Bill de Beaubien for reporting! + + + Server was incorrectly including contained resources being returned as both contained resources, and as + top-level resources in the returned bundle for search operations. + Thanks to Bill de Beaubien for reporting! This also fixes Issue #20, thanks to + lephty for reporting! + + + Documentation fixes + + + Add a collection of new methods on the generic client which support the + read, + read, + and search + ]]> + operations using an absolute URL. This allows developers to perform these operations using + URLs they obtained from other sources (or external resource references within resources). In + addition, the existing read/vread operations will now access absolute URL references if + they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics + for contributing this implementation! + + + Server implementation was not correctly figuring out its own FHIR Base URL when deployed + on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of + Systems Made Simple for their help in figuring out this issue! + + + XML Parser failed to encode fields with both a resource reference child and + a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of + Systems Made Simple for their help in figuring out this issue! + + + HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to + Bernard Gitaadji for reporting and diagnosing the issue! + + + Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of + Orion Health for reporting this! + + + Conformance profiles which are automatically generated by the server were missing a few mandatory elements, + which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple + for reporting this! + + + XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters + escaped properly in encoded messages. + + + Resources containing entities which are not valid in basic XML (e.g. &sect;) will have those + entities converted to their equivalent unicode characters when resources are encoded, since FHIR does + not allow extended entities in resource instances. + + + Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers) + to client requests. + + + Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on + some platforms. Thanks to Odysseas Pentakalos for the patch! + + + HAPI now logs a single line indicating the StAX implementation being used upon the + first time an XML parser is created. + + + Update methods on the server did not return a "content-location" header, but + only a "location" header. Both are required according to the FHIR specification. + Thanks to Bill de Beaubien of Systems Made Simple for reporting this! + + + Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for + the patch! + + + Calling encode multiple times on a resource with contained resources caused the contained + resources to be re-added (and the actual message to grow) with each encode pass. Thanks to + Alexander Kley for the test case! + + + JSON-encoded contained resources with the incorrect "_id" element (which should be "id", but some + incorrect examples exist on the FHIR specification) now parse correctly. In other words, HAPI + previously only accepted the correct "id" element, but now it also accepts the incorrect + "_id" element just to be more lenient. + + + Several unit tests failed on Windows (or any platform with non UTF-8 default encoding). This may + have also caused resource validation to fail occasionally on these platforms as well. + Thanks to Bill de Beaubien for reporting! + + + toString() method on TokenParam was incorrectly showing the system as the value. + Thanks to Bill de Beaubien for reporting! + + + Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks + to David Hay of Orion Health for reporting! + + + Add a + Vagrant]]> + based environment (basically a fully built, self contained development environment) for + trying out the HAPI server modules. Thanks to Preston Lee for the pull request, and for + offering to maintain this! + + + Change validation API so that it uses a return type instead of exceptions to communicate + validation failures. Thanks to Joe Athman for the pull request! + + + Add a client interceptor which adds an HTTP cookie to each client request. Thanks to + Petro Mykhailysyn for the pull request! + + + + + + Add server interceptor framework, and new interceptor for logging incoming + requests. + + + Add server validation framework for validating resources against the FHIR schemas and schematrons + + + Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University + Health Network for reporting! + + + Create method was incorrectly returning an HTTP 204 on sucessful completion, but + should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng + for reporting! + + + FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing + non US-ASCII characters will correctly display in the browser + + + JSON parser was incorrectly encoding extensions on composite elements outside the element itself + (as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of + Orion for reporting this! + + + Contained/included resource instances received by a client are now automatically + added to any ResourceReferenceDt instancea in other resources which reference them. + + + Add documentation on how to use eBay CORS Filter to support Cross Origin Resource + Sharing (CORS) to server. CORS support that was built in to the server itself has + been removed, as it did not work correctly (and was reinventing a wheel that others + have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance + in testing this! + + + IResource interface did not expose the getLanguage/setLanguage methods from BaseResource, + so the resource language was difficult to access. + + + JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use + of single quotes + + + Transaction server method is now allowed to return an OperationOutcome in addition to the + incoming resources. The public test server now does this in order to return status information + about the transaction processing. + + + Update method in the server can now flag (via a field on the MethodOutcome object being returned) + that the result was actually a creation, and Create method can indicate that it was actually an + update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the + response, but this may be useful in some circumstances. + + + Annotation client search methods with a specific resource type (e.g. List<Patient> search()) + won't return any resources that aren't of the correct type that are received in a response + bundle (generally these are referenced resources, so they are populated in the reference fields instead). + Thanks to Tahura Chaudhry of University Health Network for the unit test! + + + Added narrative generator template for OperationOutcome resource + + + Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format + is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple + for reporting! + + + Server search method for an unnamed query gets called if the client requests a named query + with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting! + + + Category header (for tags) is correctly read in client for "read" operation + + + Transaction method in server can now have parameter type Bundle instead of + List<IResource> + + + HAPI parsers now use field access to get/set values instead of method accessors and mutators. + This should give a small performance boost. + + + JSON parser encodes resource references incorrectly, using the name "resource" instead + of the name "reference" for the actual reference. Thanks to + Ricky Nguyen for reporting and tracking down the issue! + + + Rename NotImpementedException to NotImplementedException (to correct typo) + + + Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status) + + + Fix performance issue in date/time datatypes where pattern matchers were not static + + + Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but + previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting! + + + Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health + for reporting! + + + QuantityParam correctly encodes approximate (~) prefix to values + + + If a server defines a method with parameter "_id", incoming search requests for that method may + get delegated to the wrong method. Thanks to Neal Acharya for reporting! + + + SecurityEvent.Object structural element has been renamed to + SecurityEvent.ObjectElement to avoid conflicting names with the + java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for + reporting! + + + Text/narrative blocks that were created with a non-empty + namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>) + failed to encode correctly (prefix was missing in encoded resource) + + + Resource references previously encoded their children (display and reference) + in the wrong order so references with both would fail schema validation. + + + SecurityEvent resource's enums now use friendly enum names instead of the unfriendly + numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the + suggestion! + + + + + HAPI has a number of RESTful method parameter types that have similar but not identical + purposes and confusing names. A cleanup has been undertaken to clean this up. + This means that a number of existing classes + have been deprocated in favour of new naming schemes. +
    ]]> + All annotation-based clients and all server search method parameters are now named + (type)Param, for example: StringParam, TokenParam, etc. +
    ]]> + All generic/fluent client method parameters are now named + (type)ClientParam, for example: StringClientParam, TokenClientParam, etc. +
    ]]> + All renamed classes have been retained and deprocated, so this change should not cause any issues + for existing applications but those applications should be refactored to use the + new parameters when possible. +
    + + Allow server methods to return wildcard generic types (e.g. List<? extends IResource>) + + + Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as + "&identifier=system|codepart1\|codepart2" + + + Add support for OPTIONS verb (which returns the server conformance statement) + + + Add support for CORS headers in server + + + Bump SLF4j dependency to latest version (1.7.7) + + + Add interceptor framework for clients (annotation based and generic), and add interceptors + for configurable logging, capturing requests and responses, and HTTP basic auth. + + + Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead + of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one! + + + Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion! + + + If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or + an OperationOutcome resource, include the message in the exception message so that it will be + more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion! + + + Read invocations in the client now process the "Content-Location" header and use it to + populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion! + + + Fix issue where vread invocations on server incorrectly get routed to instance history method if one is + defined. Thanks to Neal Acharya from UHN for surfacing this one! + + + Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary + blobs from being used for nefarious purposes. See + FHIR Tracker Bug 3298]]> + for more information. + + + Support has been added for using an HTTP proxy for outgoing requests. + + + Fix: Primitive extensions declared against custom resource types + are encoded even if they have no value. Thanks to David Hay of Orion for + reporting this! + + + Fix: RESTful server deployed to a location where the URL to access it contained a + space (e.g. a WAR file with a space in the name) failed to work correctly. + Thanks to David Hay of Orion for reporting this! + +
    + + + BREAKING CHANGE:]]>: IdDt has been modified so that it + contains a partial or complete resource identity. Previously it contained + only the simple alphanumeric id of the resource (the part at the end of the "read" URL for + that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123") + and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have + been added to this datatype which provide just the numeric portion. See the JavaDoc + for more information. + + + API CHANGE:]]>: Most elements in the HAPI FHIR model contain + a getId() and setId() method. This method is confusing because it is only actually used + for IDREF elements (which are rare) but its name makes it easy to confuse with more + important identifiers. For this reason, these methods have been deprocated and replaced with + get/setElementSpecificId() methods. The old methods will be removed at some point. Resource + types are unchanged and retain their get/setId methods. + + + Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously + QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to + support quantity search parameters on the server (e.g. Observation.value-quantity) + + + Introduce StringParameter type which can be used as a RESTful operation search parameter + type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers. + + + Parsers (XML/JSON) now support deleted entries in bundles + + + Transaction method now supported in servers + + + Support for Binary resources added (in servers, clients, parsers, etc.) + + + Support for Query resources fixed (in parser) + + + Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource) + now parse and encode correctly, meaning that all contained resources are placed in the "contained" element + of the root resource, and the parser looks in the root resource for all container levels when stitching + contained resources back together. + + + Server methods with @Include parameter would sometimes fail when no _include was actually + specified in query strings. + + + Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct + query string if the system is null. + + + Add support for paging responses from RESTful servers. + + + Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are + produced by the Health Intersections server) + + + Server now automatically compresses responses if the client indicates support + + + Server failed to support optional parameters when type is String and :exact qualifier is used + + + Read method in client correctly populated resource ID in returned object + + + Support added for deleted-entry by/name, by/email, and comment from Tombstones spec + + + + + + + + + +
    diff --git a/src/site/fml/hapi-fhir-faq.fml b/src/site/fml/hapi-fhir-faq.fml index 6122d411250..87d90f4013e 100644 --- a/src/site/fml/hapi-fhir-faq.fml +++ b/src/site/fml/hapi-fhir-faq.fml @@ -81,6 +81,23 @@ + + Using HAPI + + + What JDK version does HAPI support? + + +

    + HAPI supports JDK 1.6 for the entire library, except for the CLI tool which is 1.8. +

    +

    + Note that the HAPI library itself also requires a 1.8 JDK to build, since the unit tests + have JDK 1.8 dependencies. +

    +
    +
    +
    JPA Server diff --git a/src/site/site.xml b/src/site/site.xml index 8fbadbce93c..ad25cf7ad8d 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -57,6 +57,7 @@ + @@ -141,7 +142,9 @@ + diff --git a/src/site/xdoc/doc_jpa.xml b/src/site/xdoc/doc_jpa.xml index 1d09cae835f..c1528ca79e1 100644 --- a/src/site/xdoc/doc_jpa.xml +++ b/src/site/xdoc/doc_jpa.xml @@ -177,13 +177,6 @@ public DaoConfig daoConfig() { -
    -

    - The documentation for the JPA server is not yet complete. Please get in touch - if you are able to help us complete it! -

    -
    -
    Architecture diff --git a/src/site/xdoc/doc_rest_client_examples.xml b/src/site/xdoc/doc_rest_client_examples.xml index 570b7ef1bbf..91bf9665bb9 100644 --- a/src/site/xdoc/doc_rest_client_examples.xml +++ b/src/site/xdoc/doc_rest_client_examples.xml @@ -53,7 +53,7 @@ - + diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml index 7cfe7137dde..c2df19fc577 100644 --- a/src/site/xdoc/index.xml +++ b/src/site/xdoc/index.xml @@ -57,6 +57,15 @@ + + +

    + Commercial support for HAPI FHIR is available through + Smile CDR. +

    + +
    +