Add suport for composite and sorting to JPA (not yet complete)

This commit is contained in:
jamesagnew 2014-07-29 09:10:25 -04:00
parent 318dcfcaaf
commit 0bc703107c
72 changed files with 3347 additions and 1862 deletions

View File

@ -6,7 +6,6 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin" path="src/site/example/java"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>

View File

@ -2,6 +2,5 @@
<wb-module deploy-name="hapi-fhir-base">
<wb-resource deploy-path="/" source-path="/src/main/java"/>
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/src/site/example/java"/>
</wb-module>
</project-modules>

View File

@ -75,6 +75,11 @@
are encoded even if they have no value. Thanks to David Hay of Orion for
reporting this!
</action>
<action type="fix">
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!
</action>
</release>
<release version="0.4" date="2014-Jul-13">
<action type="add">

View File

@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -48,7 +49,6 @@ import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IExtension;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.IResourceBlock;
@ -299,8 +299,6 @@ class ModelScanner {
scanCompositeElementForChildren(theClass, resourceDef);
}
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement
}
@ -602,6 +600,9 @@ class ModelScanner {
private void scanResourceForSearchParams(Class<? extends IResource> theClass, RuntimeResourceDefinition theResourceDef) {
Map<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
for (Field nextField : theClass.getFields()) {
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
if (searchParam != null) {
@ -609,11 +610,34 @@ class ModelScanner {
if (paramType == null) {
throw new ConfigurationException("Searc param " + searchParam.name() + " has an invalid type: " + searchParam.type());
}
if(paramType==SearchParamTypeEnum.COMPOSITE) {
compositeFields.put(nextField, searchParam);
continue;
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType);
theResourceDef.addSearchParam(param);
nameToParam.put(param.getName(), param);
}
}
for (Entry<Field, SearchParamDefinition> nextEntry : compositeFields.entrySet()) {
Field nextField = nextEntry.getKey();
SearchParamDefinition searchParam = nextEntry.getValue();
List<RuntimeSearchParam> compositeOf = new ArrayList<RuntimeSearchParam>();
for (String nextName:searchParam.compositeOf()) {
RuntimeSearchParam param = nameToParam.get(nextName);
if (param==null) {
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
new Object[] {theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet()});
continue;
}
compositeOf.add(param);
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), SearchParamTypeEnum.COMPOSITE, compositeOf);
theResourceDef.addSearchParam(param);
}
}
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.context;
import java.util.List;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
/*
@ -22,30 +24,29 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
* #L%
*/
public class RuntimeSearchParam {
private String myDescription;
private String myName;
private String myPath;
private SearchParamTypeEnum myParamType;
// private List<String> myPathParts;
private String myPath;
private List<RuntimeSearchParam> myCompositeOf;
public RuntimeSearchParam(String theName, String theDescription, String thePath, SearchParamTypeEnum theParamType) {
this(theName, theDescription, thePath, theParamType, null);
}
public RuntimeSearchParam(String theName, String theDescription, String thePath, SearchParamTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf) {
super();
myName = theName;
myDescription = theDescription;
myPath=thePath;
myParamType=theParamType;
// myPathParts = Arrays.asList(thePath.split("\\."));
myPath = thePath;
myParamType = theParamType;
myCompositeOf = theCompositeOf;
}
public SearchParamTypeEnum getParamType() {
return myParamType;
}
public String getPath() {
return myPath;
public List<RuntimeSearchParam> getCompositeOf() {
return myCompositeOf;
}
public String getDescription() {
@ -56,5 +57,12 @@ public class RuntimeSearchParam {
return myName;
}
public SearchParamTypeEnum getParamType() {
return myParamType;
}
public String getPath() {
return myPath;
}
}

View File

@ -39,7 +39,10 @@ public @interface SearchParamDefinition {
/**
* If the parameter is of type "composite", this parameter lists the names of the parameters
* which this parameter is a composite of. E.g. "name-value-token" is a composite of "name" and "value-token"
* which this parameter is a composite of. E.g. "name-value-token" is a composite of "name" and "value-token".
* <p>
* If the parameter is not a composite, this parameter must be empty
* </p>
*/
String[] compositeOf() default {};

View File

@ -16,26 +16,6 @@
package ca.uhn.fhir.model.dstu.resource;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.Date;
import java.util.List;
@ -63,13 +43,8 @@ import ca.uhn.fhir.model.dstu.composite.RatioDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.composite.SampledDataDt;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationInterpretationCodesEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationRelationshipTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationReliabilityEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationStatusEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -113,7 +88,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.name</b><br/>
* </p>
*/
@SearchParamDefinition(name="name", path="Observation.name", description="The name of the observation type", type="token")
@SearchParamDefinition(name="name", path="Observation.name", description="The name of the observation type", type="token" )
public static final String SP_NAME = "name";
/**
@ -134,7 +109,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.value[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="value-quantity", path="Observation.value[x]", description="The value of the observation, if the value is a Quantity, or a SampledData (just search on the bounds of the values in sampled data)", type="quantity")
@SearchParamDefinition(name="value-quantity", path="Observation.value[x]", description="The value of the observation, if the value is a Quantity, or a SampledData (just search on the bounds of the values in sampled data)", type="quantity" )
public static final String SP_VALUE_QUANTITY = "value-quantity";
/**
@ -155,7 +130,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.value[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="value-concept", path="Observation.value[x]", description="The value of the observation, if the value is a CodeableConcept", type="token")
@SearchParamDefinition(name="value-concept", path="Observation.value[x]", description="The value of the observation, if the value is a CodeableConcept", type="token" )
public static final String SP_VALUE_CONCEPT = "value-concept";
/**
@ -176,7 +151,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.value[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="value-date", path="Observation.value[x]", description="The value of the observation, if the value is a Period", type="date")
@SearchParamDefinition(name="value-date", path="Observation.value[x]", description="The value of the observation, if the value is a Period", type="date" )
public static final String SP_VALUE_DATE = "value-date";
/**
@ -197,7 +172,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.value[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="value-string", path="Observation.value[x]", description="The value of the observation, if the value is a string, and also searches in CodeableConcept.text", type="string")
@SearchParamDefinition(name="value-string", path="Observation.value[x]", description="The value of the observation, if the value is a string, and also searches in CodeableConcept.text", type="string" )
public static final String SP_VALUE_STRING = "value-string";
/**
@ -210,27 +185,6 @@ public class Observation extends BaseResource implements IResource {
*/
public static final StringClientParam VALUE_STRING = new StringClientParam(SP_VALUE_STRING);
/**
* Search parameter constant for <b>name-value-[x]</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="name-value-[x]", path="name & value-[x]", description="Both name and one of the value parameters", type="composite")
public static final String SP_NAME_VALUE_X = "name-value-[x]";
/**
* <b>Fluent Client</b> search parameter constant for <b>name-value-[x]</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
public static final CompositeClientParam NAME_VALUE_X = new CompositeClientParam(SP_NAME_VALUE_X);
/**
* Search parameter constant for <b>date</b>
* <p>
@ -239,7 +193,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.applies[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="date", path="Observation.applies[x]", description="Obtained date/time. If the obtained element is a period, a date that falls in the period", type="date")
@SearchParamDefinition(name="date", path="Observation.applies[x]", description="Obtained date/time. If the obtained element is a period, a date that falls in the period", type="date" )
public static final String SP_DATE = "date";
/**
@ -260,7 +214,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.status</b><br/>
* </p>
*/
@SearchParamDefinition(name="status", path="Observation.status", description="The status of the observation", type="token")
@SearchParamDefinition(name="status", path="Observation.status", description="The status of the observation", type="token" )
public static final String SP_STATUS = "status";
/**
@ -281,7 +235,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.reliability</b><br/>
* </p>
*/
@SearchParamDefinition(name="reliability", path="Observation.reliability", description="The reliability of the observation", type="token")
@SearchParamDefinition(name="reliability", path="Observation.reliability", description="The reliability of the observation", type="token" )
public static final String SP_RELIABILITY = "reliability";
/**
@ -302,7 +256,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.subject</b><br/>
* </p>
*/
@SearchParamDefinition(name="subject", path="Observation.subject", description="The subject that the observation is about", type="reference")
@SearchParamDefinition(name="subject", path="Observation.subject", description="The subject that the observation is about", type="reference" )
public static final String SP_SUBJECT = "subject";
/**
@ -329,7 +283,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.performer</b><br/>
* </p>
*/
@SearchParamDefinition(name="performer", path="Observation.performer", description="Who and/or what performed the observation", type="reference")
@SearchParamDefinition(name="performer", path="Observation.performer", description="Who and/or what performed the observation", type="reference" )
public static final String SP_PERFORMER = "performer";
/**
@ -356,7 +310,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.specimen</b><br/>
* </p>
*/
@SearchParamDefinition(name="specimen", path="Observation.specimen", description="", type="reference")
@SearchParamDefinition(name="specimen", path="Observation.specimen", description="", type="reference" )
public static final String SP_SPECIMEN = "specimen";
/**
@ -383,7 +337,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.related.type</b><br/>
* </p>
*/
@SearchParamDefinition(name="related-type", path="Observation.related.type", description="", type="token")
@SearchParamDefinition(name="related-type", path="Observation.related.type", description="", type="token" )
public static final String SP_RELATED_TYPE = "related-type";
/**
@ -404,7 +358,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.related.target</b><br/>
* </p>
*/
@SearchParamDefinition(name="related-target", path="Observation.related.target", description="", type="reference")
@SearchParamDefinition(name="related-target", path="Observation.related.target", description="", type="reference" )
public static final String SP_RELATED_TARGET = "related-target";
/**
@ -424,25 +378,109 @@ public class Observation extends BaseResource implements IResource {
public static final Include INCLUDE_RELATED_TARGET = new Include("Observation.related.target");
/**
* Search parameter constant for <b>related</b>
* Search parameter constant for <b>name-value-quantity</b>
* <p>
* Description: <b>Related Observations - search on related-type and related-target together</b><br/>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>related-target & related-type</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="related", path="related-target & related-type", description="Related Observations - search on related-type and related-target together", type="composite")
public static final String SP_RELATED = "related";
@SearchParamDefinition(name="name-value-quantity", path="name & value-[x]", description="Both name and one of the value parameters", type="composite" , compositeOf={ "name", "value-quantity" } )
public static final String SP_NAME_VALUE_QUANTITY = "name-value-quantity";
/**
* <b>Fluent Client</b> search parameter constant for <b>related</b>
* <b>Fluent Client</b> search parameter constant for <b>name-value-quantity</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
public static final CompositeClientParam<TokenClientParam, QuantityClientParam> NAME_VALUE_QUANTITY = new CompositeClientParam<TokenClientParam, QuantityClientParam>(SP_NAME_VALUE_QUANTITY);
/**
* Search parameter constant for <b>name-value-concept</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="name-value-concept", path="name & value-[x]", description="Both name and one of the value parameters", type="composite" , compositeOf={ "name", "value-concept" } )
public static final String SP_NAME_VALUE_CONCEPT = "name-value-concept";
/**
* <b>Fluent Client</b> search parameter constant for <b>name-value-concept</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
public static final CompositeClientParam<TokenClientParam, TokenClientParam> NAME_VALUE_CONCEPT = new CompositeClientParam<TokenClientParam, TokenClientParam>(SP_NAME_VALUE_CONCEPT);
/**
* Search parameter constant for <b>name-value-date</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="name-value-date", path="name & value-[x]", description="Both name and one of the value parameters", type="composite" , compositeOf={ "name", "value-date" } )
public static final String SP_NAME_VALUE_DATE = "name-value-date";
/**
* <b>Fluent Client</b> search parameter constant for <b>name-value-date</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
public static final CompositeClientParam<TokenClientParam, DateClientParam> NAME_VALUE_DATE = new CompositeClientParam<TokenClientParam, DateClientParam>(SP_NAME_VALUE_DATE);
/**
* Search parameter constant for <b>name-value-string</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
@SearchParamDefinition(name="name-value-string", path="name & value-[x]", description="Both name and one of the value parameters", type="composite" , compositeOf={ "name", "value-string" } )
public static final String SP_NAME_VALUE_STRING = "name-value-string";
/**
* <b>Fluent Client</b> search parameter constant for <b>name-value-string</b>
* <p>
* Description: <b>Both name and one of the value parameters</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>name & value-[x]</b><br/>
* </p>
*/
public static final CompositeClientParam<TokenClientParam, StringClientParam> NAME_VALUE_STRING = new CompositeClientParam<TokenClientParam, StringClientParam>(SP_NAME_VALUE_STRING);
/**
* Search parameter constant for <b>related-target-related-type</b>
* <p>
* Description: <b>Related Observations - search on related-type and related-target together</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>related-target & related-type</b><br/>
* </p>
*/
public static final CompositeClientParam RELATED = new CompositeClientParam(SP_RELATED);
@SearchParamDefinition(name="related-target-related-type", path="related-target & related-type", description="Related Observations - search on related-type and related-target together", type="composite" , compositeOf={ "related-target", "related-type" } )
public static final String SP_RELATED_TARGET_RELATED_TYPE = "related-target-related-type";
/**
* <b>Fluent Client</b> search parameter constant for <b>related-target-related-type</b>
* <p>
* Description: <b>Related Observations - search on related-type and related-target together</b><br/>
* Type: <b>composite</b><br/>
* Path: <b>related-target & related-type</b><br/>
* </p>
*/
public static final CompositeClientParam<ReferenceClientParam, TokenClientParam> RELATED_TARGET_RELATED_TYPE = new CompositeClientParam<ReferenceClientParam, TokenClientParam>(SP_RELATED_TARGET_RELATED_TYPE);
@Child(name="name", type=CodeableConceptDt.class, order=0, min=1, max=1)
@ -465,7 +503,7 @@ public class Observation extends BaseResource implements IResource {
shortDefinition="High, low, normal, etc.",
formalDefinition="The assessment made based on the result of the observation."
)
private BoundCodeableConceptDt<ObservationInterpretationCodesEnum> myInterpretation;
private CodeableConceptDt myInterpretation;
@Child(name="comments", type=StringDt.class, order=3, min=0, max=1)
@Description(
@ -494,14 +532,14 @@ public class Observation extends BaseResource implements IResource {
shortDefinition="registered | preliminary | final | amended +",
formalDefinition="The status of the result value"
)
private BoundCodeDt<ObservationStatusEnum> myStatus;
private CodeDt myStatus;
@Child(name="reliability", type=CodeDt.class, order=7, min=1, max=1)
@Description(
shortDefinition="ok | ongoing | early | questionable | calibrating | error +",
formalDefinition="An estimate of the degree to which quality issues have impacted on the value reported"
)
private BoundCodeDt<ObservationReliabilityEnum> myReliability;
private CodeDt myReliability;
@Child(name="bodySite", type=CodeableConceptDt.class, order=8, min=0, max=1)
@Description(
@ -642,9 +680,9 @@ public class Observation extends BaseResource implements IResource {
* The assessment made based on the result of the observation.
* </p>
*/
public BoundCodeableConceptDt<ObservationInterpretationCodesEnum> getInterpretation() {
public CodeableConceptDt getInterpretation() {
if (myInterpretation == null) {
myInterpretation = new BoundCodeableConceptDt<ObservationInterpretationCodesEnum>(ObservationInterpretationCodesEnum.VALUESET_BINDER);
myInterpretation = new CodeableConceptDt();
}
return myInterpretation;
}
@ -657,24 +695,11 @@ public class Observation extends BaseResource implements IResource {
* The assessment made based on the result of the observation.
* </p>
*/
public Observation setInterpretation(BoundCodeableConceptDt<ObservationInterpretationCodesEnum> theValue) {
public Observation setInterpretation(CodeableConceptDt theValue) {
myInterpretation = theValue;
return this;
}
/**
* Sets the value(s) for <b>interpretation</b> (High, low, normal, etc.)
*
* <p>
* <b>Definition:</b>
* The assessment made based on the result of the observation.
* </p>
*/
public Observation setInterpretation(ObservationInterpretationCodesEnum theValue) {
getInterpretation().setValueAsEnum(theValue);
return this;
}
/**
* Gets the value(s) for <b>comments</b> (Comments about result).
@ -786,8 +811,8 @@ public class Observation extends BaseResource implements IResource {
*
* </p>
*/
public Observation setIssued( Date theDate, TemporalPrecisionEnum thePrecision) {
myIssued = new InstantDt(theDate, thePrecision);
public Observation setIssuedWithMillisPrecision( Date theDate) {
myIssued = new InstantDt(theDate);
return this;
}
@ -799,8 +824,8 @@ public class Observation extends BaseResource implements IResource {
*
* </p>
*/
public Observation setIssuedWithMillisPrecision( Date theDate) {
myIssued = new InstantDt(theDate);
public Observation setIssued( Date theDate, TemporalPrecisionEnum thePrecision) {
myIssued = new InstantDt(theDate, thePrecision);
return this;
}
@ -815,9 +840,9 @@ public class Observation extends BaseResource implements IResource {
* The status of the result value
* </p>
*/
public BoundCodeDt<ObservationStatusEnum> getStatus() {
public CodeDt getStatus() {
if (myStatus == null) {
myStatus = new BoundCodeDt<ObservationStatusEnum>(ObservationStatusEnum.VALUESET_BINDER);
myStatus = new CodeDt();
}
return myStatus;
}
@ -830,25 +855,25 @@ public class Observation extends BaseResource implements IResource {
* The status of the result value
* </p>
*/
public Observation setStatus(BoundCodeDt<ObservationStatusEnum> theValue) {
public Observation setStatus(CodeDt theValue) {
myStatus = theValue;
return this;
}
/**
* Sets the value(s) for <b>status</b> (registered | preliminary | final | amended +)
/**
* Sets the value for <b>status</b> (registered | preliminary | final | amended +)
*
* <p>
* <b>Definition:</b>
* The status of the result value
* </p>
*/
public Observation setStatus(ObservationStatusEnum theValue) {
getStatus().setValueAsEnum(theValue);
return this;
public Observation setStatus( String theCode) {
myStatus = new CodeDt(theCode);
return this;
}
/**
* Gets the value(s) for <b>reliability</b> (ok | ongoing | early | questionable | calibrating | error +).
* creating it if it does
@ -859,9 +884,9 @@ public class Observation extends BaseResource implements IResource {
* An estimate of the degree to which quality issues have impacted on the value reported
* </p>
*/
public BoundCodeDt<ObservationReliabilityEnum> getReliability() {
public CodeDt getReliability() {
if (myReliability == null) {
myReliability = new BoundCodeDt<ObservationReliabilityEnum>(ObservationReliabilityEnum.VALUESET_BINDER);
myReliability = new CodeDt();
}
return myReliability;
}
@ -874,25 +899,25 @@ public class Observation extends BaseResource implements IResource {
* An estimate of the degree to which quality issues have impacted on the value reported
* </p>
*/
public Observation setReliability(BoundCodeDt<ObservationReliabilityEnum> theValue) {
public Observation setReliability(CodeDt theValue) {
myReliability = theValue;
return this;
}
/**
* Sets the value(s) for <b>reliability</b> (ok | ongoing | early | questionable | calibrating | error +)
/**
* Sets the value for <b>reliability</b> (ok | ongoing | early | questionable | calibrating | error +)
*
* <p>
* <b>Definition:</b>
* An estimate of the degree to which quality issues have impacted on the value reported
* </p>
*/
public Observation setReliability(ObservationReliabilityEnum theValue) {
getReliability().setValueAsEnum(theValue);
return this;
public Observation setReliability( String theCode) {
myReliability = new CodeDt(theCode);
return this;
}
/**
* Gets the value(s) for <b>bodySite</b> (Observed body part).
* creating it if it does
@ -993,8 +1018,8 @@ public class Observation extends BaseResource implements IResource {
* A unique identifier for the simple observation
* </p>
*/
public Observation setIdentifier( IdentifierUseEnum theUse, String theSystem, String theValue, String theLabel) {
myIdentifier = new IdentifierDt(theUse, theSystem, theValue, theLabel);
public Observation setIdentifier( String theSystem, String theValue) {
myIdentifier = new IdentifierDt(theSystem, theValue);
return this;
}
@ -1006,8 +1031,8 @@ public class Observation extends BaseResource implements IResource {
* A unique identifier for the simple observation
* </p>
*/
public Observation setIdentifier( String theSystem, String theValue) {
myIdentifier = new IdentifierDt(theSystem, theValue);
public Observation setIdentifier( IdentifierUseEnum theUse, String theSystem, String theValue, String theLabel) {
myIdentifier = new IdentifierDt(theUse, theSystem, theValue, theLabel);
return this;
}
@ -1324,32 +1349,6 @@ public class Observation extends BaseResource implements IResource {
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, double theValue, String theSystem, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, long theValue, String theSystem, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, double theValue, String theUnits) {
@ -1363,19 +1362,6 @@ public class Observation extends BaseResource implements IResource {
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, long theValue, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theUnits);
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( double theValue) {
@ -1396,6 +1382,45 @@ public class Observation extends BaseResource implements IResource {
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, long theValue, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theUnits);
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, double theValue, String theSystem, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>low</b> (Low Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the low bound of the reference range. If this is omitted, the low bound of the reference range is assumed to be meaningless. E.g. <2.3
* </p>
*/
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, long theValue, String theSystem, String theUnits) {
myLow = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Gets the value(s) for <b>high</b> (High Range, if relevant).
@ -1433,32 +1458,6 @@ public class Observation extends BaseResource implements IResource {
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, double theValue, String theSystem, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, long theValue, String theSystem, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, double theValue, String theUnits) {
@ -1472,19 +1471,6 @@ public class Observation extends BaseResource implements IResource {
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, long theValue, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theUnits);
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( double theValue) {
@ -1505,6 +1491,45 @@ public class Observation extends BaseResource implements IResource {
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, long theValue, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theUnits);
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, double theValue, String theSystem, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Sets the value for <b>high</b> (High Range, if relevant)
*
* <p>
* <b>Definition:</b>
* The value of the high bound of the reference range. If this is omitted, the high bound of the reference range is assumed to be meaningless. E.g. >5
* </p>
*/
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, long theValue, String theSystem, String theUnits) {
myHigh = new QuantityDt(theComparator, theValue, theSystem, theUnits);
return this;
}
/**
* Gets the value(s) for <b>meaning</b> (Indicates the meaning/use of this range of this range).
@ -1588,7 +1613,7 @@ public class Observation extends BaseResource implements IResource {
shortDefinition="has-component | has-member | derived-from | sequel-to | replaces | qualified-by | interfered-by",
formalDefinition="A code specifying the kind of relationship that exists with the target observation"
)
private BoundCodeDt<ObservationRelationshipTypeEnum> myType;
private CodeDt myType;
@Child(name="target", order=1, min=1, max=1, type={
ca.uhn.fhir.model.dstu.resource.Observation.class })
@ -1619,9 +1644,9 @@ public class Observation extends BaseResource implements IResource {
* A code specifying the kind of relationship that exists with the target observation
* </p>
*/
public BoundCodeDt<ObservationRelationshipTypeEnum> getType() {
public CodeDt getType() {
if (myType == null) {
myType = new BoundCodeDt<ObservationRelationshipTypeEnum>(ObservationRelationshipTypeEnum.VALUESET_BINDER);
myType = new CodeDt();
}
return myType;
}
@ -1634,25 +1659,25 @@ public class Observation extends BaseResource implements IResource {
* A code specifying the kind of relationship that exists with the target observation
* </p>
*/
public Related setType(BoundCodeDt<ObservationRelationshipTypeEnum> theValue) {
public Related setType(CodeDt theValue) {
myType = theValue;
return this;
}
/**
* Sets the value(s) for <b>type</b> (has-component | has-member | derived-from | sequel-to | replaces | qualified-by | interfered-by)
/**
* Sets the value for <b>type</b> (has-component | has-member | derived-from | sequel-to | replaces | qualified-by | interfered-by)
*
* <p>
* <b>Definition:</b>
* A code specifying the kind of relationship that exists with the target observation
* </p>
*/
public Related setType(ObservationRelationshipTypeEnum theValue) {
getType().setValueAsEnum(theValue);
return this;
public Related setType( String theCode) {
myType = new CodeDt(theCode);
return this;
}
/**
* Gets the value(s) for <b>target</b> (Observation that is related to this one).
* creating it if it does
@ -1687,7 +1712,15 @@ public class Observation extends BaseResource implements IResource {
}
public void setStatus(ObservationStatusEnum theFinal) {
if (theFinal==null) {
getStatus().setValue(null);
}else {
getStatus().setValue(theFinal.getCode());
}
}
}
}

View File

@ -35,11 +35,12 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs,
* Resource References, etc. to represent a specific instance of a resource.
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any
* other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -52,7 +53,7 @@ public class IdDt extends BasePrimitive<String> {
private String myResourceType;
private String myUnqualifiedId;
private String myUnqualifiedVersionId;
private String myValue;
private volatile String myValue;
/**
* Create a new empty ID
@ -62,7 +63,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string
* representation.
*/
public IdDt(BigDecimal thePid) {
if (thePid != null) {
@ -80,11 +82,12 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete URL (http://example.com/fhir/Patient/1234).
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete
* URL (http://example.com/fhir/Patient/1234).
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -107,13 +110,6 @@ public class IdDt extends BasePrimitive<String> {
this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
}
private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
if (theIdPart==null) {
throw new NullPointerException("BigDecimal ID can not be null");
}
return theIdPart.toPlainString();
}
/**
* Constructor
*
@ -144,7 +140,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is
* ambiguous)
*/
public BigDecimal asBigDecimal() {
return getIdPartAsBigDecimal();
@ -165,7 +162,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns a reference to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
* Returns a reference to <code>this</code> IdDt. It is generally not neccesary to use this method but it is
* provided for consistency with the rest of the API.
*/
@Override
public IdDt getId() {
@ -209,18 +207,27 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a
* simple ID. Use {@link #getIdPart()} to get just the ID portion.
*
* @see #getIdPart()
*/
@Override
public String getValue() {
if (myValue == null && myHaveComponentParts) {
if (myUnqualifiedVersionId != null) {
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId;
} else {
myValue = myResourceType + '/' + myUnqualifiedId;
StringBuilder b = new StringBuilder();
if (isNotBlank(myResourceType)) {
b.append(myResourceType);
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(myUnqualifiedVersionId);
}
myValue = b.toString();
}
return myValue;
}
@ -255,7 +262,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
*/
public boolean isIdPartValidLong() {
String id = getIdPart();
@ -278,7 +286,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method
* but it is provided for consistency with the rest of the API.
*/
@Override
public void setId(IdDt theId) {
@ -289,8 +298,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -300,7 +309,7 @@ public class IdDt extends BasePrimitive<String> {
public void setValue(String theValue) throws DataFormatException {
// TODO: add validation
myValue = theValue;
myHaveComponentParts=false;
myHaveComponentParts = false;
if (StringUtils.isBlank(theValue)) {
myValue = null;
myUnqualifiedId = null;
@ -337,8 +346,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -372,9 +381,15 @@ public class IdDt extends BasePrimitive<String> {
}
}
public IdDt withResourceType(String theResourceName) {
return new IdDt(theResourceName, getIdPart(), getVersionIdPart());
}
/**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially,
* because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL.
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be
* used if the ID does not already contain those respective parts). Essentially, because IdDt can contain either a
* complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete
* URL.
*
* @param theServerBase
* The server base (e.g. "http://example.com/fhir")
@ -398,29 +413,31 @@ public class IdDt extends BasePrimitive<String> {
}
retVal.append('/');
retVal.append(getIdPart());
if (hasVersionIdPart()) {
retVal.append('/');
retVal.append(Constants.PARAM_HISTORY);
retVal.append('/');
retVal.append(getVersionIdPart());
retVal.append(getVersionIdPart());
}
return retVal.toString();
}
/**
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion.
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID
* noted by theVersion.
*
* @param theVersion
* The actual version string, e.g. "1"
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion.
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted
* by theVersion.
*/
public IdDt withVersion(String theVersion) {
Validate.notBlank(theVersion, "Version may not be null or empty");
String existingValue = getValue();
int i = existingValue.indexOf(Constants.PARAM_HISTORY);
String value;
if (i > 1) {
@ -432,8 +449,11 @@ public class IdDt extends BasePrimitive<String> {
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
}
public IdDt withResourceType(String theResourceName) {
return new IdDt(theResourceName, getIdPart(), getVersionIdPart());
private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
if (theIdPart == null) {
throw new NullPointerException("BigDecimal ID can not be null");
}
return theIdPart.toPlainString();
}
}

View File

@ -59,6 +59,18 @@ public class InstantDt extends BaseDateTimeDt {
setTimeZone(theCalendar.getTimeZone());
}
/**
* Create a new DateTimeDt using an existing value. <b>Use this constructor with caution</b>,
* as it may create more precision than warranted (since for example it is possible to pass in
* a DateTime with only a year, and this constructor will convert to an InstantDt with
* milliseconds precision).
*/
public InstantDt(BaseDateTimeDt theDateTime) {
setValue(theDateTime.getValue());
setPrecision(DEFAULT_PRECISION);
setTimeZone(theDateTime.getTimeZone());
}
/**
* Create a new DateTimeDt
*/

View File

@ -23,18 +23,55 @@ package ca.uhn.fhir.rest.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME)
/**
* Parameter annotation which specifies a search parameter for a {@link Search} method.
*/
public @interface OptionalParam {
String name();
/**
* This is the name for the parameter. Generally this should be a
* simple string (e.g. "name", or "identifier") which will be the name
* of the URL parameter used to populate this method parameter.
* <p>
* Most resource model classes have constants which may be used to
* supply values for this field, e.g. {@link Patient#SP_NAME} or
* {@link Observation#SP_DATE}
* </p>
* <p>
* If you wish to specify a parameter for a resource reference which
* only accepts a specific chained value, it is also valid to supply
* a chained name here, such as "patient.name". It is recommended to
* supply this using constants where possible, e.g.
* <code>{@link Observation#SP_SUBJECT} + '.' + {@link Patient#SP_IDENTIFIER}</code>
* </p>
*/
String name();
/**
* For resource reference parameters ({@link ReferenceParam}) this parameter may be
* used to indicate the resource type(s) which may be referenced by this param
* used to indicate the resource type(s) which may be referenced by this param.
* <p>
* If the parameter annotated with this annotation is not a {@link ReferenceParam},
* this value must not be populated.
* </p>
*/
Class<? extends IResource>[] targetTypes() default {};
/**
* For composite parameters ({@link CompositeParam}) this parameter may be
* used to indicate the parameter type(s) which may be referenced by this param.
* <p>
* If the parameter annotated with this annotation is not a {@link CompositeParam},
* this value must not be populated.
* </p>
*/
Class<? extends IQueryParameterType>[] compositeTypes() default {};
}

View File

@ -23,18 +23,56 @@ package ca.uhn.fhir.rest.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME)
/**
* Parameter annotation which specifies a search parameter for a {@link Search} method.
*/
public @interface RequiredParam {
String name();
/**
* This is the name for the parameter. Generally this should be a
* simple string (e.g. "name", or "identifier") which will be the name
* of the URL parameter used to populate this method parameter.
* <p>
* Most resource model classes have constants which may be used to
* supply values for this field, e.g. {@link Patient#SP_NAME} or
* {@link Observation#SP_DATE}
* </p>
* <p>
* If you wish to specify a parameter for a resource reference which
* only accepts a specific chained value, it is also valid to supply
* a chained name here, such as "patient.name". It is recommended to
* supply this using constants where possible, e.g.
* <code>{@link Observation#SP_SUBJECT} + '.' + {@link Patient#SP_IDENTIFIER}</code>
* </p>
*/
String name();
/**
* For resource reference parameters ({@link ReferenceParam}) this parameter may be
* used to indicate the resource type(s) which may be referenced by this param
* used to indicate the resource type(s) which may be referenced by this param.
* <p>
* If the parameter annotated with this annotation is not a {@link ReferenceParam},
* this value must not be populated.
* </p>
*/
Class<? extends IResource>[] targetTypes() default {};
/**
* For composite parameters ({@link CompositeParam}) this parameter may be
* used to indicate the parameter type(s) which may be referenced by this param.
* <p>
* If the parameter annotated with this annotation is not a {@link CompositeParam},
* this value must not be populated.
* </p>
*/
Class<? extends IQueryParameterType>[] compositeTypes() default {};
}

View File

@ -67,6 +67,8 @@ import ca.uhn.fhir.rest.gclient.ISort;
import ca.uhn.fhir.rest.gclient.ITransaction;
import ca.uhn.fhir.rest.gclient.ITransactionTyped;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
import ca.uhn.fhir.rest.gclient.IUpdateTyped;
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
@ -77,7 +79,6 @@ import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.ReadMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
import ca.uhn.fhir.rest.method.UpdateMethodBinding;
import ca.uhn.fhir.rest.method.ValidateMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -278,7 +279,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
BaseHttpClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -344,6 +345,24 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this;
}
protected IResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = null;
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
switch (theResourceBody.charAt(i)) {
case '<':
encoding = EncodingEnum.XML;
break;
case '{':
encoding = EncodingEnum.JSON;
break;
}
}
if (encoding == null) {
throw new InvalidRequestException("FHIR client can't determine resource encoding");
}
return encoding.newParser(myContext).parseResource(theResourceBody);
}
@SuppressWarnings("unchecked")
@Override
public T encodedJson() {
@ -419,23 +438,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public MethodOutcome execute() {
if (myResource == null) {
EncodingEnum encoding = null;
for (int i = 0; i < myResourceBody.length() && encoding == null; i++) {
switch (myResourceBody.charAt(i)) {
case '<':
encoding = EncodingEnum.XML;
break;
case '{':
encoding = EncodingEnum.JSON;
break;
}
}
if (encoding == null) {
throw new InvalidRequestException("FHIR client can't determine resource encoding");
}
myResource = encoding.newParser(myContext).parseResource(myResourceBody);
myResource = parseResourceBody(myResourceBody);
}
myId = getPreferredId(myResource, myId);
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
@ -448,6 +454,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public ICreateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
@ -475,6 +482,76 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private class UpdateInternal extends BaseClientExecutable<IUpdateTyped, MethodOutcome> implements IUpdate, IUpdateTyped {
private IdDt myId;
private String myResourceBody;
private IResource myResource;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
if (myId == null) {
myId = myResource.getId();
}
if (myId==null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
}
@Override
public IUpdateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IUpdateTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IUpdateTyped withId(IdDt theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart()==false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: "+theId.getValue());
}
myId = theId;
return this;
}
@Override
public IUpdateTyped withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: "+theId);
}
myId=new IdDt(theId);
return this;
}
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, OperationOutcome> implements IDelete, IDeleteTyped {
@ -924,4 +1001,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
protected String getPreferredId(IResource theResource, String theId) {
if (isNotBlank(theId)) {
return theId;
}
return theResource.getId().getIdPart();
}
@Override
public IUpdate update() {
return new UpdateInternal();
}
}

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.gclient.IGetPage;
import ca.uhn.fhir.rest.gclient.IGetTags;
import ca.uhn.fhir.rest.gclient.ITransaction;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
public interface IGenericClient {
/**
@ -268,4 +269,9 @@ public interface IGenericClient {
*/
IDelete delete();
/**
* Fluent method for the "update" operation, which performs a logical delete on a server resource
*/
IUpdate update();
}

View File

@ -21,23 +21,25 @@ package ca.uhn.fhir.rest.gclient;
*/
/**
* Composite params are not yet supported - This is a placeholder. Help welcome!
*
* TODO: implement
* Composite parameter type for use in fluent client interfaces
*/
public class CompositeClientParam {
public class CompositeClientParam<A extends IParam, B extends IParam> implements IParam {
private String myName;
public CompositeClientParam(String theName) {
// TODO Auto-generated constructor stub
myName=theName;
}
/**
* Composite params are not yet supported - This is a placeholder. Help welcome!
*
* TODO: implement
*/
public void notYetSupported() {
//nothig
@Override
public String getParamName() {
return myName;
}
public ICompositeWithLeft<B> withLeft(ICriterion<A> theLeft) {
return new CompositeCriterion<A,B>(myName, theLeft);
}
}

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.rest.gclient;
import static org.apache.commons.lang3.StringUtils.*;
public class CompositeCriterion<A extends IParam, B extends IParam> implements ICompositeWithLeft<B>, ICriterion<B>, ICriterionInternal {
private ICriterion<B> myRight;
private String myName;
private ICriterion<A> myLeft;
public CompositeCriterion(String theName, ICriterion<A> theLeft) {
myName = theName;
myLeft = theLeft;
}
@Override
public ICriterion<B> withRight(ICriterion<B> theRight) {
myRight = theRight;
return this;
}
@Override
public String getParameterValue() {
ICriterionInternal left = (ICriterionInternal) myLeft;
ICriterionInternal right = (ICriterionInternal) myRight;
return defaultString(left.getParameterValue()) + '$' + defaultString(right.getParameterValue());
}
@Override
public String getParameterName() {
return myName;
}
}

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.rest.gclient;
/**
* @deprecated Use {@link CompositeClientParam} instead. That class is identical to this one but was renamed to reduct
* @deprecated Use {@link CompositeClientParam} instead. That class is identical to this one but was renamed to reduce
* confusing duplicate names in the API. This class will be removed in a future release.
*/
public class CompositeParam extends CompositeClientParam {
public class CompositeParam<A extends IParam, B extends IParam> extends CompositeClientParam<A,B> {
public CompositeParam(String theParamName) {
super(theParamName);

View File

@ -61,7 +61,7 @@ public class DateClientParam implements IParam {
return new DateWithPrefix("");
}
private class Criterion implements ICriterion, ICriterionInternal {
private class Criterion implements ICriterion<DateClientParam>, ICriterionInternal {
private String myValue;
@ -89,35 +89,35 @@ public class DateClientParam implements IParam {
}
@Override
public ICriterion day(Date theValue) {
public ICriterion<DateClientParam> day(Date theValue) {
DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString());
}
@Override
public ICriterion day(String theValue) {
public ICriterion<DateClientParam> day(String theValue) {
DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString());
}
@Override
public ICriterion now() {
public ICriterion<DateClientParam> now() {
DateTimeDt dt = new DateTimeDt();
dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString());
}
@Override
public ICriterion second(Date theValue) {
public ICriterion<DateClientParam> second(Date theValue) {
DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString());
}
@Override
public ICriterion second(String theValue) {
public ICriterion<DateClientParam> second(String theValue) {
DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString());
@ -127,15 +127,15 @@ public class DateClientParam implements IParam {
public interface IDateSpecifier {
ICriterion day(Date theValue);
ICriterion<DateClientParam> day(Date theValue);
ICriterion day(String theValue);
ICriterion<DateClientParam> day(String theValue);
ICriterion now();
ICriterion<DateClientParam> now();
ICriterion second(Date theValue);
ICriterion<DateClientParam> second(Date theValue);
ICriterion second(String theValue);
ICriterion<DateClientParam> second(String theValue);
}

View File

@ -0,0 +1,7 @@
package ca.uhn.fhir.rest.gclient;
public interface ICompositeWithLeft<B extends IParam> {
ICriterion<B> withRight(ICriterion<B> theRight);
}

View File

@ -20,6 +20,6 @@ package ca.uhn.fhir.rest.gclient;
* #L%
*/
public interface ICriterion {
public interface ICriterion<T extends IParam> {
}

View File

@ -25,9 +25,9 @@ import ca.uhn.fhir.model.api.Include;
public interface IQuery extends IClientExecutable<IQuery,Bundle> {
IQuery where(ICriterion theCriterion);
IQuery where(ICriterion<?> theCriterion);
IQuery and(ICriterion theCriterion);
IQuery and(ICriterion<?> theCriterion);
IQuery include(Include theIncludeManagingorganization);

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.model.api.IResource;
public interface IUpdate {
IUpdateTyped resource(IResource theResource);
IUpdateTyped resource(String theResourceBody);
}

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
public interface IUpdateTyped extends IClientExecutable<IUpdateTyped, MethodOutcome> {
IUpdateTyped withId(IdDt theId);
IUpdateTyped withId(String theId);
}

View File

@ -31,16 +31,16 @@ public class NumberClientParam implements IParam {
myParamName = theParamName;
}
public IMatches<ICriterion> exactly() {
return new IMatches<ICriterion>() {
public IMatches<ICriterion<NumberClientParam>> exactly() {
return new IMatches<ICriterion<NumberClientParam>>() {
@Override
public ICriterion number(long theNumber) {
return new StringCriterion(getParamName(), Long.toString(theNumber));
public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), Long.toString(theNumber));
}
@Override
public ICriterion number(String theNumber) {
return new StringCriterion(getParamName(), (theNumber));
public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), (theNumber));
}
};
}
@ -50,58 +50,58 @@ public class NumberClientParam implements IParam {
return myParamName;
}
public IMatches<ICriterion> greaterThan() {
return new IMatches<ICriterion>() {
public IMatches<ICriterion<NumberClientParam>> greaterThan() {
return new IMatches<ICriterion<NumberClientParam>>() {
@Override
public ICriterion number(long theNumber) {
return new StringCriterion(getParamName(), ">" + Long.toString(theNumber));
public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), ">" + Long.toString(theNumber));
}
@Override
public ICriterion number(String theNumber) {
return new StringCriterion(getParamName(), ">" + (theNumber));
public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), ">" + (theNumber));
}
};
}
public IMatches<ICriterion> greaterThanOrEqual() {
return new IMatches<ICriterion>() {
public IMatches<ICriterion<NumberClientParam>> greaterThanOrEqual() {
return new IMatches<ICriterion<NumberClientParam>>() {
@Override
public ICriterion number(long theNumber) {
return new StringCriterion(getParamName(), ">=" + Long.toString(theNumber));
public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), ">=" + Long.toString(theNumber));
}
@Override
public ICriterion number(String theNumber) {
return new StringCriterion(getParamName(), ">=" + (theNumber));
public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), ">=" + (theNumber));
}
};
}
public IMatches<ICriterion> lessThan() {
return new IMatches<ICriterion>() {
public IMatches<ICriterion<NumberClientParam>> lessThan() {
return new IMatches<ICriterion<NumberClientParam>>() {
@Override
public ICriterion number(long theNumber) {
return new StringCriterion(getParamName(), "<" + Long.toString(theNumber));
public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), "<" + Long.toString(theNumber));
}
@Override
public ICriterion number(String theNumber) {
return new StringCriterion(getParamName(), "<" + (theNumber));
public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), "<" + (theNumber));
}
};
}
public IMatches<ICriterion> lessThanOrEqual() {
return new IMatches<ICriterion>() {
public IMatches<ICriterion<NumberClientParam>> lessThanOrEqual() {
return new IMatches<ICriterion<NumberClientParam>>() {
@Override
public ICriterion number(long theNumber) {
return new StringCriterion(getParamName(), "<=" + Long.toString(theNumber));
public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), "<=" + Long.toString(theNumber));
}
@Override
public ICriterion number(String theNumber) {
return new StringCriterion(getParamName(), "<=" + (theNumber));
public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion<NumberClientParam>(getParamName(), "<=" + (theNumber));
}
};
}

View File

@ -141,11 +141,11 @@ public class QuantityClientParam implements IParam {
public interface IAndUnits {
ICriterion andNoUnits();
ICriterion<QuantityClientParam> andNoUnits();
ICriterion andUnits(String theUnits);
ICriterion<QuantityClientParam> andUnits(String theUnits);
ICriterion andUnits(String theSystem, String theUnits);
ICriterion<QuantityClientParam> andUnits(String theSystem, String theUnits);
}
private class AndUnits implements IAndUnits {
@ -157,18 +157,18 @@ public class QuantityClientParam implements IParam {
}
@Override
public ICriterion andNoUnits() {
public ICriterion<QuantityClientParam> andNoUnits() {
return andUnits(null, null);
}
@Override
public ICriterion andUnits(String theUnits) {
public ICriterion<QuantityClientParam> andUnits(String theUnits) {
return andUnits(theUnits, null);
}
@Override
public ICriterion andUnits(String theSystem, String theUnits) {
return new StringCriterion(getParamName(), myToken1 + "|" + defaultString(theSystem) + "|" + defaultString(theUnits));
public ICriterion<QuantityClientParam> andUnits(String theSystem, String theUnits) {
return new QuantityCriterion(getParamName(), myToken1 , defaultString(theSystem) , defaultString(theUnits));
}
}

View File

@ -0,0 +1,49 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.param.ParameterUtil;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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%
*/
class QuantityCriterion implements ICriterion<QuantityClientParam>, ICriterionInternal {
private String myValue;
private String myName;
private String mySystem;
private String myUnits;
public QuantityCriterion(String theParamName, String theValue, String theSystem, String theUnits) {
myValue = theValue;
myName = theParamName;
mySystem = theSystem;
myUnits = theUnits;
}
@Override
public String getParameterName() {
return myName;
}
@Override
public String getParameterValue() {
return ParameterUtil.escape(myValue) + '|' + ParameterUtil.escape(mySystem) + '|' + ParameterUtil.escape(myUnits);
}
}

View File

@ -36,32 +36,32 @@ public class ReferenceClientParam implements IParam {
return myName;
}
public ICriterion hasChainedProperty(ICriterion theICriterion) {
return new ReferenceChainCriterion(getParamName(), theICriterion);
public ICriterion<ReferenceClientParam> hasChainedProperty(ICriterion<?> theCriterion) {
return new ReferenceChainCriterion(getParamName(), theCriterion);
}
/**
* Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource)
*/
public ICriterion hasId(IdDt theId) {
return new StringCriterion(getParamName(), theId.getValue());
public ICriterion<ReferenceClientParam> hasId(IdDt theId) {
return new StringCriterion<ReferenceClientParam>(getParamName(), theId.getValue());
}
/**
* Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource)
*/
public ICriterion hasId(String theId) {
return new StringCriterion(getParamName(), theId);
public ICriterion<ReferenceClientParam> hasId(String theId) {
return new StringCriterion<ReferenceClientParam>(getParamName(), theId);
}
private static class ReferenceChainCriterion implements ICriterion, ICriterionInternal {
private static class ReferenceChainCriterion implements ICriterion<ReferenceClientParam>, ICriterionInternal {
private String myParamName;
private ICriterionInternal myWrappedCriterion;
public ReferenceChainCriterion(String theParamName, ICriterion theWrappedCriterion) {
public ReferenceChainCriterion(String theParamName, ICriterion<?> theWrappedCriterion) {
myParamName = theParamName;
myWrappedCriterion = (ICriterionInternal) theWrappedCriterion;
}

View File

@ -59,33 +59,33 @@ public class StringClientParam implements IParam {
public interface IStringMatch {
ICriterion value(String theValue);
ICriterion<StringClientParam> value(String theValue);
ICriterion value(StringDt theValue);
ICriterion<StringClientParam> value(StringDt theValue);
}
private class StringExactly implements IStringMatch {
@Override
public ICriterion value(String theValue) {
return new StringCriterion(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue);
public ICriterion<StringClientParam> value(String theValue) {
return new StringCriterion<StringClientParam>(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue);
}
@Override
public ICriterion value(StringDt theValue) {
return new StringCriterion(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue.getValue());
public ICriterion<StringClientParam> value(StringDt theValue) {
return new StringCriterion<StringClientParam>(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue.getValue());
}
}
private class StringMatches implements IStringMatch {
@Override
public ICriterion value(String theValue) {
return new StringCriterion(getParamName(), theValue);
public ICriterion<StringClientParam> value(String theValue) {
return new StringCriterion<StringClientParam>(getParamName(), theValue);
}
@Override
public ICriterion value(StringDt theValue) {
return new StringCriterion(getParamName(), theValue.getValue());
public ICriterion<StringClientParam> value(StringDt theValue) {
return new StringCriterion<StringClientParam>(getParamName(), theValue.getValue());
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.param.ParameterUtil;
/*
* #%L
* HAPI FHIR - Core Library
@ -20,7 +22,7 @@ package ca.uhn.fhir.rest.gclient;
* #L%
*/
class StringCriterion implements ICriterion, ICriterionInternal {
class StringCriterion<A extends IParam> implements ICriterion<A>, ICriterionInternal {
private String myValue;
private String myName;
@ -37,7 +39,7 @@ class StringCriterion implements ICriterion, ICriterionInternal {
@Override
public String getParameterValue() {
return myValue;
return ParameterUtil.escape(myValue);
}
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.gclient;
import static org.apache.commons.lang3.StringUtils.*;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
/*
@ -42,27 +43,27 @@ public class TokenClientParam implements IParam {
public IMatches exactly() {
return new IMatches() {
@Override
public ICriterion systemAndCode(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), theSystem, theCode);
public ICriterion<TokenClientParam> systemAndCode(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
}
@Override
public ICriterion systemAndIdentifier(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), theSystem, theCode);
public ICriterion<TokenClientParam> systemAndIdentifier(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
}
@Override
public ICriterion code(String theCode) {
public ICriterion<TokenClientParam> code(String theCode) {
return new TokenCriterion(getParamName(), null, theCode);
}
@Override
public ICriterion identifier(String theIdentifier) {
public ICriterion<TokenClientParam> identifier(String theIdentifier) {
return new TokenCriterion(getParamName(), null, theIdentifier);
}
@Override
public ICriterion identifier(IdentifierDt theIdentifier) {
public ICriterion<TokenClientParam> identifier(IdentifierDt theIdentifier) {
return new TokenCriterion(getParamName(), theIdentifier.getSystem().getValueAsString(), theIdentifier.getValue().getValue());
}
};
@ -78,7 +79,7 @@ public class TokenClientParam implements IParam {
* The code
* @return A criterion
*/
ICriterion systemAndCode(String theSystem, String theCode);
ICriterion<TokenClientParam> systemAndCode(String theSystem, String theCode);
/**
* Creates a search criterion that matches against the given system and identifier
@ -89,7 +90,7 @@ public class TokenClientParam implements IParam {
* The identifier
* @return A criterion
*/
ICriterion systemAndIdentifier(String theSystem, String theIdentifier);
ICriterion<TokenClientParam> systemAndIdentifier(String theSystem, String theIdentifier);
/**
* Creates a search criterion that matches against the given identifier, with no system specified
@ -98,7 +99,7 @@ public class TokenClientParam implements IParam {
* The identifier
* @return A criterion
*/
ICriterion identifier(String theIdentifier);
ICriterion<TokenClientParam> identifier(String theIdentifier);
/**
* Creates a search criterion that matches against the given code, with no code system specified
@ -107,7 +108,7 @@ public class TokenClientParam implements IParam {
* The identifier
* @return A criterion
*/
ICriterion code(String theIdentifier);
ICriterion<TokenClientParam> code(String theIdentifier);
/**
* Creates a search criterion that matches against the given identifier (system and code if both are present, or whatever is present)
@ -116,7 +117,7 @@ public class TokenClientParam implements IParam {
* The identifier
* @return A criterion
*/
ICriterion identifier(IdentifierDt theIdentifier);
ICriterion<TokenClientParam> identifier(IdentifierDt theIdentifier);
}
}

View File

@ -22,17 +22,23 @@ package ca.uhn.fhir.rest.gclient;
import org.apache.commons.lang3.StringUtils;
class TokenCriterion implements ICriterion, ICriterionInternal {
import ca.uhn.fhir.rest.param.ParameterUtil;
class TokenCriterion implements ICriterion<TokenClientParam>, ICriterionInternal {
private String myValue;
private String myName;
public TokenCriterion(String theName, String theSystem, String theCode) {
myName = theName;
if (StringUtils.isNotBlank(theSystem)) {
myValue = theSystem + "|" + StringUtils.defaultString(theCode);
String system = ParameterUtil.escape(theSystem);
String code = ParameterUtil.escape(theCode);
if (StringUtils.isNotBlank(system)) {
myValue = system + "|" + StringUtils.defaultString(code);
} else if (system == null) {
myValue = StringUtils.defaultString(code);
} else {
myValue = "|" + StringUtils.defaultString(theCode);
myValue = "|" + StringUtils.defaultString(code);
}
}

View File

@ -0,0 +1,60 @@
package ca.uhn.fhir.rest.method;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
class BaseBinder<T> {
private Class<? extends IQueryParameterType>[] myCompositeTypes;
private Constructor<? extends T> myConstructor;
private final Class<? extends T> myType;
public BaseBinder(Class<? extends T> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
myType = theType;
myCompositeTypes = theCompositeTypes;
if (myType.equals(CompositeParam.class)) {
if (myCompositeTypes.length != 2) {
throw new ConfigurationException("Search parameter of type " + myType.getName() + " must have 2 composite types declared in parameter annotation, found " + theCompositeTypes.length);
}
}
try {
Class<?>[] types = new Class<?>[myCompositeTypes.length];
for (int i = 0; i < myCompositeTypes.length; i++) {
types[i] = myCompositeTypes[i].getClass();
}
myConstructor = myType.getConstructor(types);
} catch (NoSuchMethodException e) {
throw new ConfigurationException("Query parameter type " + theType.getName() + " has no constructor with types " + Arrays.asList(theCompositeTypes));
}
}
public T newInstance() {
try {
final Object[] args = new Object[myCompositeTypes.length];
for (int i = 0; i < myCompositeTypes.length;i++) {
args[i] = myCompositeTypes[i];//.newInstance();
}
T dt = myConstructor.newInstance(args);
return dt;
} catch (final InstantiationException e) {
throw new InternalErrorException(e);
} catch (final IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (final SecurityException e) {
throw new InternalErrorException(e);
} catch (final IllegalArgumentException e) {
throw new InternalErrorException(e);
} catch (final InvocationTargetException e) {
throw new InternalErrorException(e);
}
}
}

View File

@ -79,13 +79,15 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
}
myResourceType = theReturnResourceType;
if (theReturnResourceType != null) {
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
if (resourceDefAnnotation == null) {
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
if (IResource.class.isAssignableFrom(theReturnResourceType)) {
myResourceType = theReturnResourceType;
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
if (resourceDefAnnotation == null) {
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
}
myResourceName = resourceDefAnnotation.name();
}
myResourceName = resourceDefAnnotation.name();
}
}
@ -192,7 +194,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
Integer count = RestfulServer.extractCountParameter(theRequest.getServletRequest());
boolean respondGzip=theRequest.isRespondGzip();
boolean respondGzip = theRequest.isRespondGzip();
IBundleProvider result = invokeServer(theRequest, params);
switch (getReturnType()) {
@ -210,7 +212,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
/**
* Subclasses may override
*

View File

@ -33,6 +33,10 @@ public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContent
super(theContext, theResource, theUrlExtension);
}
public HttpPutClientInvocation(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlExtension) {
super(theContext,theContents, theIsBundle, theUrlExtension);
}
@Override
protected HttpRequestBase createRequest(String url, AbstractHttpEntity theEntity) {
HttpPut retVal = new HttpPut(url);

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.PushbackReader;
@ -36,6 +36,7 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.TagListParam;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.Count;
@ -81,6 +82,37 @@ public class MethodUtil {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, String theResourceBody, IdDt theId, FhirContext theContext) {
String resourceName = theContext.getResourceDefinition(theResource).getName();
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(resourceName);
urlBuilder.append('/');
urlBuilder.append(theId.getIdPart());
HttpPutClientInvocation retVal;
String urlExtension = urlBuilder.toString();
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension);
} else {
retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension);
}
if (theId.hasVersionIdPart()) {
String versionId = theId.getVersionIdPart();
if (StringUtils.isNotBlank(versionId)) {
urlBuilder.append('/');
urlBuilder.append(Constants.PARAM_HISTORY);
urlBuilder.append('/');
urlBuilder.append(versionId);
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, urlBuilder.toString());
}
}
addTagsToPostOrPut(theResource, retVal);
return retVal;
}
public static void parseClientRequestResourceHeaders(Map<String, List<String>> theHeaders, IResource resource) {
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
@ -298,6 +330,7 @@ public class MethodUtil {
parameter.setName(((RequiredParam) nextAnnotation).name());
parameter.setRequired(true);
parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations);
param = parameter;
@ -306,6 +339,7 @@ public class MethodUtil {
parameter.setName(((OptionalParam) nextAnnotation).name());
parameter.setRequired(false);
parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations);
param = parameter;

View File

@ -25,14 +25,14 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterAndBinder implements IParamBinder {
private final Class<? extends IQueryParameterAnd<?>> myType;
final class QueryParameterAndBinder extends BaseBinder<IQueryParameterAnd<?>> implements IParamBinder {
QueryParameterAndBinder(Class<? extends IQueryParameterAnd<?>> theType) {
myType = theType;
QueryParameterAndBinder(Class<? extends IQueryParameterAnd<?>> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
super(theType, theCompositeTypes);
}
@SuppressWarnings("unchecked")
@ -46,12 +46,8 @@ final class QueryParameterAndBinder implements IParamBinder {
public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterAnd<?> dt;
try {
dt = myType.newInstance();
dt = newInstance();
dt.setValuesAsQueryTokens(theString);
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) {
throw new InternalErrorException(e);
}

View File

@ -25,14 +25,14 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterOrBinder implements IParamBinder {
private final Class<? extends IQueryParameterOr<?>> myType;
final class QueryParameterOrBinder extends BaseBinder<IQueryParameterOr<?>> implements IParamBinder {
QueryParameterOrBinder(Class<? extends IQueryParameterOr<?>> theType) {
myType = theType;
QueryParameterOrBinder(Class<? extends IQueryParameterOr<?>> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
super(theType, theCompositeTypes);
}
@SuppressWarnings("unchecked")
@ -47,7 +47,7 @@ final class QueryParameterOrBinder implements IParamBinder {
public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterOr<?> dt;
try {
dt = myType.newInstance();
dt = newInstance();
if (theString.size() == 0 || theString.get(0).size() == 0) {
return dt;
}
@ -56,10 +56,6 @@ final class QueryParameterOrBinder implements IParamBinder {
}
dt.setValuesAsQueryTokens(theString.get(0));
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) {
throw new InternalErrorException(e);
}

View File

@ -31,11 +31,10 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterTypeBinder implements IParamBinder {
private final Class<? extends IQueryParameterType> myType;
final class QueryParameterTypeBinder extends BaseBinder<IQueryParameterType> implements IParamBinder {
QueryParameterTypeBinder(Class<? extends IQueryParameterType> theType) {
myType = theType;
QueryParameterTypeBinder(Class<? extends IQueryParameterType> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
super(theType, theCompositeTypes);
}
@SuppressWarnings("unchecked")
@ -53,24 +52,16 @@ final class QueryParameterTypeBinder implements IParamBinder {
return null;
}
IQueryParameterType dt;
try {
dt = myType.newInstance();
if (theParams.size() == 0 || theParams.get(0).size() == 0) {
return dt;
}
if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected");
}
dt.setValueAsQueryToken(theParams.get(0).getQualifier(), value);
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) {
throw new InternalErrorException(e);
IQueryParameterType dt = super.newInstance();
if (theParams.size() == 0 || theParams.get(0).size() == 0) {
return dt;
}
if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected");
}
dt.setValueAsQueryToken(theParams.get(0).getQualifier(), value);
return dt;
}

View File

@ -39,6 +39,9 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.CompositeAndListParam;
import ca.uhn.fhir.rest.param.CompositeOrListParam;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.DateParam;
@ -70,12 +73,46 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class SearchParameter extends BaseQueryParameter {
private static HashMap<Class<?>, SearchParamTypeEnum> ourParamTypes;
static {
ourParamTypes = new HashMap<Class<?>, SearchParamTypeEnum>();
ourParamTypes.put(StringParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(StringOrListParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(StringAndListParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(TokenParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(TokenOrListParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(TokenAndListParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(DateParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateOrListParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateAndListParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateRangeParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(QuantityParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(QuantityOrListParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(QuantityAndListParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(NumberParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(NumberOrListParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(NumberAndListParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(ReferenceParam.class, SearchParamTypeEnum.REFERENCE);
ourParamTypes.put(ReferenceOrListParam.class, SearchParamTypeEnum.REFERENCE);
ourParamTypes.put(ReferenceAndListParam.class, SearchParamTypeEnum.REFERENCE);
ourParamTypes.put(CompositeParam.class, SearchParamTypeEnum.COMPOSITE);
ourParamTypes.put(CompositeOrListParam.class, SearchParamTypeEnum.COMPOSITE);
ourParamTypes.put(CompositeAndListParam.class, SearchParamTypeEnum.COMPOSITE);
}
private Class<? extends IQueryParameterType>[] myCompositeTypes;
private Class<? extends IResource>[] myDeclaredTypes;
private String myDescription;
private String myName;
private IParamBinder myParamBinder;
private SearchParamTypeEnum myParamType;
private boolean myRequired;
private Class<?> myType;
public SearchParameter() {
@ -150,8 +187,12 @@ public class SearchParameter extends BaseQueryParameter {
return myParamBinder.parse(theString);
}
public void setCompositeTypes(Class<? extends IQueryParameterType>[] theCompositeTypes) {
myCompositeTypes = theCompositeTypes;
}
public void setDeclaredTypes(Class<? extends IResource>[] theTypes) {
myDeclaredTypes=theTypes;
myDeclaredTypes = theTypes;
}
public void setDescription(String theDescription) {
@ -166,57 +207,27 @@ public class SearchParameter extends BaseQueryParameter {
this.myRequired = required;
}
static {
ourParamTypes = new HashMap<Class<?>, SearchParamTypeEnum>();
ourParamTypes.put(StringParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(StringOrListParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(StringAndListParam.class, SearchParamTypeEnum.STRING);
ourParamTypes.put(TokenParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(TokenOrListParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(TokenAndListParam.class, SearchParamTypeEnum.TOKEN);
ourParamTypes.put(DateParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateOrListParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateAndListParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(DateRangeParam.class, SearchParamTypeEnum.DATE);
ourParamTypes.put(QuantityParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(QuantityOrListParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(QuantityAndListParam.class, SearchParamTypeEnum.QUANTITY);
ourParamTypes.put(NumberParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(NumberOrListParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(NumberAndListParam.class, SearchParamTypeEnum.NUMBER);
ourParamTypes.put(ReferenceParam.class, SearchParamTypeEnum.REFERENCE);
ourParamTypes.put(ReferenceOrListParam.class, SearchParamTypeEnum.REFERENCE);
ourParamTypes.put(ReferenceAndListParam.class, SearchParamTypeEnum.REFERENCE);
}
@SuppressWarnings({ "unchecked" })
public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
this.myType = type;
if (IQueryParameterType.class.isAssignableFrom(type)) {
myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) type);
myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) type, myCompositeTypes);
} else if (IQueryParameterOr.class.isAssignableFrom(type)) {
myParamBinder = new QueryParameterOrBinder((Class<? extends IQueryParameterOr<?>>) type);
myParamBinder = new QueryParameterOrBinder((Class<? extends IQueryParameterOr<?>>) type,myCompositeTypes);
} else if (IQueryParameterAnd.class.isAssignableFrom(type)) {
myParamBinder = new QueryParameterAndBinder((Class<? extends IQueryParameterAnd<?>>) type);
myParamBinder = new QueryParameterAndBinder((Class<? extends IQueryParameterAnd<?>>) type, myCompositeTypes);
} else if (String.class.equals(type)) {
myParamBinder = new StringBinder();
myParamType=SearchParamTypeEnum.STRING;
myParamType = SearchParamTypeEnum.STRING;
} else {
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
}
if (myParamType==null) {
myParamType=ourParamTypes.get(type);
if (myParamType == null) {
myParamType = ourParamTypes.get(type);
}
if (myParamType!=null) {
if (myParamType != null) {
// ok
} else if (StringDt.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.STRING;

View File

@ -26,8 +26,6 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
@ -42,10 +40,9 @@ import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
private Integer myIdParameterIndex;
private Integer myVersionIdParameterIndex;
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
@ -112,13 +109,15 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
throw new NullPointerException("ID can not be null");
}
IdDt versionIdDt = null;
if (myVersionIdParameterIndex != null) {
versionIdDt = (IdDt) theArgs[myVersionIdParameterIndex];
IdDt versionIdDt = (IdDt) theArgs[myVersionIdParameterIndex];
if (idDt.hasVersionIdPart() == false) {
idDt = idDt.withVersion(versionIdDt.getIdPart());
}
}
FhirContext context = getContext();
HttpPutClientInvocation retVal = createUpdateInvocation(theResource, idDt, versionIdDt, context);
HttpPutClientInvocation retVal = MethodUtil.createUpdateInvocation(theResource, null,idDt, context);
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
@ -128,40 +127,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return retVal;
}
public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) {
String resourceName = context.getResourceDefinition(theResource).getName();
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
urlExtension.append('/');
urlExtension.append(idDt.getIdPart());
HttpPutClientInvocation retVal = new HttpPutClientInvocation(context, theResource, urlExtension.toString());
if (idDt.hasVersionIdPart()) {
String versionId = idDt.getVersionIdPart();
if (StringUtils.isNotBlank(versionId)) {
StringBuilder b = new StringBuilder();
// b.append('/');
b.append(urlExtension);
b.append("/_history/");
b.append(versionId);
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
}
} else if (versionIdDt != null) {
String versionId = versionIdDt.getValue();
if (StringUtils.isNotBlank(versionId)) {
StringBuilder b = new StringBuilder();
// b.append('/');
b.append(urlExtension);
b.append("/_history/");
b.append(versionId);
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
}
}
MethodUtil.addTagsToPostOrPut(theResource, retVal);
return retVal;
}
/*
@Override

View File

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

View File

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

View File

@ -0,0 +1,91 @@
package ca.uhn.fhir.rest.param;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.List;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class CompositeParam<A extends IQueryParameterType, B extends IQueryParameterType> implements IQueryParameterType {
private A myLeftType;
private B myRightType;
public CompositeParam(A theLeftInstance, B theRightInstance) {
myLeftType = theLeftInstance;
myRightType = theRightInstance;
}
public CompositeParam(Class<A> theLeftType, Class<B> theRightType) {
Validate.notNull(theLeftType);
Validate.notNull(theRightType);
try {
myLeftType = theLeftType.newInstance();
} catch (InstantiationException e) {
throw new ConfigurationException("Failed to instantiate type: " + myLeftType, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to instantiate type: " + myLeftType, e);
}
try {
myRightType = theRightType.newInstance();
} catch (InstantiationException e) {
throw new ConfigurationException("Failed to instantiate type: " + myRightType, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to instantiate type: " + myRightType, e);
}
}
/**
* @return Returns the left value for this parameter (the first of two parameters in this composite)
*/
public A getLeftValue() {
return myLeftType;
}
@Override
public String getQueryParameterQualifier() {
return null;
}
/**
* @return Returns the right value for this parameter (the second of two parameters in this composite)
*/
public B getRightValue() {
return myRightType;
}
@Override
public String getValueAsQueryToken() {
StringBuilder b = new StringBuilder();
if (myLeftType != null) {
b.append(myLeftType.getValueAsQueryToken());
}
b.append('$');
if (myRightType != null) {
b.append(myRightType.getValueAsQueryToken());
}
return b.toString();
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
if (isBlank(theValue)) {
myLeftType.setValueAsQueryToken(theQualifier, "");
myRightType.setValueAsQueryToken(theQualifier, "");
} else {
List<String> parts = ParameterUtil.splitParameterString(theValue, '$', false);
if (parts.size() > 2) {
throw new InvalidRequestException("Invalid value for composite parameter (only one '$' is valid for this parameter, others must be escaped). Value was: " + theValue);
}
myLeftType.setValueAsQueryToken(theQualifier, parts.get(0));
if (parts.size() > 1) {
myRightType.setValueAsQueryToken(theQualifier, parts.get(1));
}
}
}
}

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -162,4 +163,12 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return b.toString();
}
public InstantDt getValueAsInstantDt() {
return new InstantDt(getValue());
}
public DateTimeDt getValueAsDateTimeDt() {
return new DateTimeDt(getValueAsString());
}
}

View File

@ -52,23 +52,21 @@ public class ParameterUtil {
}
// public static Integer findSinceParameterIndex(Method theMethod) {
// return findParamIndex(theMethod, Since.class);
// }
public static int nonEscapedIndexOf(String theString, char theCharacter) {
for (int i =0; i < theString.length(); i++) {
if (theString.charAt(i)==theCharacter) {
if (i == 0 || theString.charAt(i-1) != '\\') {
for (int i = 0; i < theString.length(); i++) {
if (theString.charAt(i) == theCharacter) {
if (i == 0 || theString.charAt(i - 1) != '\\') {
return i;
}
}
}
return -1;
}
public static Object fromInstant(Class<?> theType, InstantDt theArgument) {
if (theType.equals(InstantDt.class)) {
if (theArgument == null) {
@ -138,19 +136,28 @@ public class ParameterUtil {
return null;
}
public static List<String> splitParameterString(String theInput, boolean theUnescapeComponents){
static List<String> splitParameterString(String theInput, boolean theUnescapeComponents) {
return splitParameterString(theInput, ',', theUnescapeComponents);
}
static List<String> splitParameterString(String theInput, char theDelimiter, boolean theUnescapeComponents) {
ArrayList<String> retVal = new ArrayList<String>();
if (theInput!=null) {
if (theInput != null) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < theInput.length(); i++) {
char next = theInput.charAt(i);
if (next == ',') {
if (i == 0 || theInput.charAt(i-1) != '\\') {
if (next == theDelimiter) {
if (i == 0) {
b.append(next);
} else {
if (b.length() > 0) {
retVal.add(b.toString());
b.setLength(0);
char prevChar = theInput.charAt(i - 1);
if (prevChar == '\\') {
b.append(next);
} else {
if (b.length() > 0) {
retVal.add(b.toString());
b.setLength(0);
}
}
}
} else {
@ -161,28 +168,27 @@ public class ParameterUtil {
retVal.add(b.toString());
}
}
if (theUnescapeComponents) {
for (int i = 0; i < retVal.size();i++) {
retVal.set(i,unescape(retVal.get(i)));
for (int i = 0; i < retVal.size(); i++) {
retVal.set(i, unescape(retVal.get(i)));
}
}
return retVal;
}
/**
* Escapes a string according to the rules for parameter escaping specified
* in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
* Escapes a string according to the rules for parameter escaping specified in the <a
* href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
*/
public static String escape(String theValue) {
if (theValue == null) {
return theValue;
}
StringBuilder b = new StringBuilder();
for (int i = 0; i < theValue.length();i++) {
for (int i = 0; i < theValue.length(); i++) {
char next = theValue.charAt(i);
switch (next) {
case '$':
@ -194,31 +200,31 @@ public class ParameterUtil {
b.append(next);
}
}
return b.toString();
}
/**
* Unescapes a string according to the rules for parameter escaping specified
* in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
* Unescapes a string according to the rules for parameter escaping specified in the <a
* href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
*/
public static String unescape(String theValue) {
if (theValue == null) {
return theValue;
}
if (theValue.indexOf('\\')==-1) {
if (theValue.indexOf('\\') == -1) {
return theValue;
}
StringBuilder b = new StringBuilder();
for (int i = 0; i < theValue.length();i++) {
for (int i = 0; i < theValue.length(); i++) {
char next = theValue.charAt(i);
if (next == '\\') {
if (i == theValue.length()-1) {
if (i == theValue.length() - 1) {
b.append(next);
} else {
switch (theValue.charAt(i+1)) {
switch (theValue.charAt(i + 1)) {
case '$':
case ',':
case '|':
@ -231,7 +237,7 @@ public class ParameterUtil {
b.append(next);
}
}
return b.toString();
}

View File

@ -27,6 +27,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.Constants;
public class StringParam implements IQueryParameterType {
@ -61,7 +62,7 @@ public class StringParam implements IQueryParameterType {
@Override
public String getValueAsQueryToken() {
return myValue;
return ParameterUtil.escape(myValue);
}
public String getValueNotNull() {
@ -91,7 +92,7 @@ public class StringParam implements IQueryParameterType {
} else {
setExact(false);
}
myValue = theValue;
myValue = ParameterUtil.unescape(theValue);
}
@Override
@ -104,4 +105,8 @@ public class StringParam implements IQueryParameterType {
return builder.toString();
}
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
}

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
/*
* #%L
@ -44,8 +45,20 @@ public class TokenOrListParam extends BaseOrListParam<TokenParam> {
return retVal;
}
/**
* Convenience method which adds a token to this OR list
* using the system and code from a coding
*/
public void add(CodingDt theCodingDt) {
add(new TokenParam(theCodingDt));
}
/**
* Convenience method which adds a token to this OR list
* using the system and value from an identifier
*/
public void add(IdentifierDt theIdentifierDt) {
add(new TokenParam(theIdentifierDt));
}
}

View File

@ -28,6 +28,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.server.Constants;
public class TokenParam implements IQueryParameterType {
@ -61,7 +63,21 @@ public class TokenParam implements IQueryParameterType {
* @param theCodingDt The coding
*/
public TokenParam(CodingDt theCodingDt) {
this(theCodingDt.getSystem().getValue().toASCIIString(), theCodingDt.getCode().getValue());
this(toSystemValue(theCodingDt.getSystem()), theCodingDt.getCode().getValue());
}
/**
* Constructor which copies the {@link IdentifierDt#getSystem() system} and {@link IdentifierDt#getValue() value} from a {@link IdentifierDt}
* instance and adds it as a parameter
*
* @param theCodingDt The coding
*/
public TokenParam(IdentifierDt theIdentifierDt) {
this(toSystemValue(theIdentifierDt.getSystem()), theIdentifierDt.getValue().getValue());
}
private static String toSystemValue(UriDt theSystem) {
return theSystem.getValueAsString();
}
@Override

View File

@ -418,7 +418,7 @@ public class RestfulServer extends HttpServlet {
IdDt versionId = null;
String operation = null;
String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
String requestPath = requestFullPath.substring(escapedLength(servletContextPath) + escapedLength(servletPath));
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1);
}
@ -599,6 +599,20 @@ public class RestfulServer extends HttpServlet {
}
/**
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
*/
private int escapedLength(String theServletPath) {
int delta = 0;
for(int i =0;i<theServletPath.length();i++) {
char next = theServletPath.charAt(i);
if (next == ' ') {
delta = delta + 2;
}
}
return theServletPath.length()+delta;
}
/**
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
* called immediately before beginning initialization of the restful server's internal init.

View File

@ -22,7 +22,9 @@ package ca.uhn.fhir.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
@ -153,7 +155,21 @@ public class FhirTerser {
}
BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
if (childElementDef == null) {
throw new DataFormatException("Found value of type[" + nextValue.getClass().getSimpleName() + "] which is not valid for field[" + nextChild.getElementName() + "] in " + childDef.getName());
StringBuilder b = new StringBuilder();
b.append("Found value of type[");
b.append(nextValue.getClass().getSimpleName());
b.append("] which is not valid for field[");
b.append(nextChild.getElementName());
b.append("] in ");
b.append(childDef.getName());
b.append(" - Valid types: ");
for (Iterator<String> iter = new TreeSet<String>(nextChild.getValidChildNames()).iterator(); iter.hasNext();) {
b.append(nextChild.getChildByName(iter.next()).getImplementingClass().getSimpleName());
if (iter.hasNext()) {
b.append(", ");
}
}
throw new DataFormatException(b.toString());
}
getAllChildElementsOfType(nextValue, childElementDef, theType, theList);
}

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
@ -55,6 +56,25 @@ client.create()
//END SNIPPET: create
}
{
//START SNIPPET: update
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John");
// To update a resource, it should have an ID set (if the resource object
// comes from the results of a previous read or search, it will already
// have one though)
patient.setId("Patient/123");
// Invoke the server create method (and send pretty-printed JSON encoding to the server
// instead of the default which is non-pretty printed XML)
client.update()
.resource(patient)
.execute();
//END SNIPPET: update
}
{
//START SNIPPET: conformance
// Retrieve the server's conformance statement and print its description
Conformance conf = client.conformance();
@ -97,6 +117,16 @@ response = client.search()
.execute();
//END SNIPPET: searchAdv
//START SNIPPET: searchComposite
response = client.search()
.forResource("Observation")
.where(Observation.NAME_VALUE_DATE
.withLeft(Observation.NAME.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01"))
)
.execute();
//END SNIPPET: searchComposite
//START SNIPPET: searchPaging
if (response.getLinkNext().isEmpty() == false) {

View File

@ -60,6 +60,7 @@ import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.client.ITestClient;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam;
@ -285,6 +286,24 @@ public List<Patient> searchByNamedQuery(@RequiredParam(name="someparam") StringP
}
//END SNIPPET: searchNamedQuery
//START SNIPPET: searchComposite
@Search()
public List<Observation> searchByComposite(
@RequiredParam(name=Observation.SP_NAME_VALUE_DATE, compositeTypes= {TokenParam.class, DateParam.class})
CompositeParam<TokenParam, DateParam> theParam) {
// Each of the two values in the composite param are accessible separately.
// In the case of Observation's name-value-date, the left is a string and
// the right is a date.
TokenParam observationName = theParam.getLeftValue();
DateParam observationValue = theParam.getRightValue();
List<Observation> retVal = new ArrayList<Observation>();
// ...populate...
return retVal;
}
//END SNIPPET: searchComposite
//START SNIPPET: searchIdentifierParam
@Search()
public List<Patient> searchByIdentifier(@RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theId) {

View File

@ -89,6 +89,7 @@
</subsection>
<subsection name="Type - Search/Query">
<p>
The following example shows how to query using the generic client:
</p>
@ -97,6 +98,8 @@
<param name="file"
value="src/site/example/java/example/GenericClientExample.java" />
</macro>
<h4>Search - Paging</h4>
<p>
If the server supports paging results, the client has a page method
which can be used to load subsequent pages.
@ -106,6 +109,21 @@
<param name="file"
value="src/site/example/java/example/GenericClientExample.java" />
</macro>
<h4>Search - Composite Parameters</h4>
<p>
If a composite parameter is being searched on, the parameter
takes a "left" and "right" operand, each of which is
a parameter from the resource being seached. The following example shows the
syntax.
</p>
<macro name="snippet">
<param name="id" value="searchComposite" />
<param name="file"
value="src/site/example/java/example/GenericClientExample.java" />
</macro>
<h4>Search - Query Options</h4>
<p>
The fluent search also has methods for sorting, limiting, specifying
JSON encoding, etc.
@ -115,6 +133,7 @@
<param name="file"
value="src/site/example/java/example/GenericClientExample.java" />
</macro>
</subsection>
<subsection name="Instance - Delete">
@ -129,6 +148,23 @@
</macro>
</subsection>
<subsection name="Instance - Update">
<p>
Updating a resource is similar to creating one, except that
an ID must be supplied since you are updating a previously
existing resource instance.
</p>
<p>
The following example shows how to perform an update
operation using the generic client:
</p>
<macro name="snippet">
<param name="id" value="update" />
<param name="file"
value="src/site/example/java/example/GenericClientExample.java" />
</macro>
</subsection>
<subsection name="Server - Conformance">
<p>
To retrieve the server's conformance statement, simply call the <code>conformance()</code>
@ -157,7 +193,7 @@
<section name="The Annotation-Driven Client">
<p>
HAPI also provides a second style of client caled the annotation-driven client.
HAPI also provides a second style of client, called the <b>annotation-driven</b> client.
</p>
<p>

View File

@ -727,6 +727,34 @@
</subsection>
<subsection name="Search Parameters: Composite">
<p>
Composite search parameters incorporate two parameters in a single
value. Each of those parameters will themselves have a parameter type.
</p>
<p>
In the following example, Observation.name-value-date is shown. This parameter
is a composite of a string and a date. Note that the composite parameter types
(StringParam and DateParam) must be specified in both the annotation's
<code>compositeTypes</code> field, as well as the generic types for the
<code>CompositeParam</code> method parameter itself.
</p>
<macro name="snippet">
<param name="id" value="searchComposite" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method:
<br />
<code>http://fhir.example.com/Observation?name-value-date=PROCTIME$2001-02-02</code>
</p>
</subsection>
<subsection name="Combining Multiple Parameters">
<p>

View File

@ -10,7 +10,6 @@ import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;

View File

@ -28,7 +28,6 @@ import ca.uhn.fhir.context.FhirContext;
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.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
@ -113,8 +112,11 @@ public class JsonParserTest {
" }" +
"}";
//@formatter:on
IResource res = ourCtx.newJsonParser().parseResource(text);
Patient res = (Patient) ourCtx.newJsonParser().parseResource(text);
String value = res.getText().getDiv().getValueAsString();
assertNull(value);
}

View File

@ -9,6 +9,7 @@ import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
@ -45,6 +46,7 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
@ -58,6 +60,7 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam;
@ -771,7 +774,7 @@ public class ClientTest {
}
@Test
public void testSearchComposite() throws Exception {
public void testSearchOrList() throws Exception {
String msg = getPatientFeedWithOneResult();
@ -944,6 +947,28 @@ public class ClientTest {
assertEquals("PRP1660", resource.getIdentifier().get(0).getValue().getValue());
}
@Test
public void testSearchByCompositeParam() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
StringParam str = new StringParam("FOO$BAR");
DateParam date = new DateParam("2001-01-01");
client.getObservationByNameValueDate(new CompositeParam<StringParam, DateParam>(str, date));
assertEquals("http://foo/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01","UTF-8"), capt.getValue().getURI().toString());
}
@Test
public void testSearchWithStringIncludes() throws Exception {

View File

@ -4,6 +4,7 @@ import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.StringReader;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
@ -15,6 +16,7 @@ import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
@ -41,6 +43,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class GenericClientTest {
@ -138,6 +141,68 @@ public class GenericClientTest {
}
@Test
public void testUpdate() throws Exception {
Patient p1 = new Patient();
p1.addIdentifier("foo:bar", "12345");
p1.addName().addFamily("Smith").addGiven("John");
TagList list = new TagList();
list.addTag("http://hl7.org/fhir/tag", "urn:happytag", "This is a happy resource");
ResourceMetadataKeyEnum.TAG_LIST.put(p1, list);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") });
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
try {
client.update().resource(p1).execute();
fail();
} catch (InvalidRequestException e) {
// should happen because no ID set
}
assertEquals(0, capt.getAllValues().size());
p1.setId("44");
client.update().resource(p1).execute();
assertEquals(1, capt.getAllValues().size());
MethodOutcome outcome = client.update().resource(p1).execute();
assertEquals("44", outcome.getId().getIdPart());
assertEquals("22", outcome.getId().getVersionIdPart());
assertEquals(2, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/44", capt.getValue().getURI().toString());
assertEquals("PUT", capt.getValue().getMethod());
Header catH = capt.getValue().getFirstHeader("Category");
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
/*
* Try fluent options
*/
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
client.update().resource(p1).withId("123").execute();
assertEquals(3, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
client.update().resource(resourceText).withId("123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(3).getURI().toString());
assertEquals(resourceText, IOUtils.toString(((HttpPut)capt.getAllValues().get(3)).getEntity().getContent()));
assertEquals(4, capt.getAllValues().size());
}
@Test
public void testDelete() throws Exception {
OperationOutcome oo = new OperationOutcome();
@ -362,6 +427,45 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?identifier=http%3A%2F%2Fexample.com%2Ffhir%7CZZZ", capt.getValue().getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
//@formatter:off
response = client.search()
.forResource("Patient")
.where(Patient.IDENTIFIER.exactly().code("ZZZ"))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?identifier=ZZZ", capt.getAllValues().get(1).getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchByComposite() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
//@formatter:off
Bundle response = client.search()
.forResource("Observation")
.where(Observation.NAME_VALUE_DATE
.withLeft(Observation.NAME.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01"))
)
.execute();
//@formatter:on
assertEquals("http://foo/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01","UTF-8"), capt.getValue().getURI().toString());
}
@Test

View File

@ -6,6 +6,7 @@ import java.util.List;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -26,6 +27,7 @@ import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam;
@ -40,7 +42,10 @@ public interface ITestClient extends IBasicClient {
@Search()
public List<Patient> getPatientByDateRange(@RequiredParam(name = "dateRange") DateRangeParam theIdentifiers);
@Search(type=Observation.class)
public Bundle getObservationByNameValueDate(@RequiredParam(name = Observation.SP_NAME_VALUE_DATE, compositeTypes= {StringParam.class,DateParam.class}) CompositeParam<StringParam, DateParam> theIdentifiers);
@Search()
public List<Patient> getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate);

View File

@ -0,0 +1,178 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.CompositeAndListParam;
import ca.uhn.fhir.rest.param.CompositeOrListParam;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class CompositeParameterTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx;
private static int ourPort;
private static Server ourServer;
@Test
public void testSearchWithDateValue() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("foo\\$bar$2001-01-01", "UTF-8"));
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Observation o = (Observation) entries.get(0).getResource();
assertEquals("foo$bar", o.getName().getText().getValue());
assertEquals("2001-01-01", ((DateTimeDt) o.getApplies()).getValueAsString().substring(0, 10));
}
}
@Test
public void testSearchWithMultipleValue() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?" + Observation.SP_NAME_VALUE_STRING + "=" + URLEncoder.encode("l1$r1,l2$r2", "UTF-8") + "&" + Observation.SP_NAME_VALUE_STRING + "=" + URLEncoder.encode("l3$r3,l4$r4", "UTF-8"));
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = ourCtx.newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Observation o = (Observation) entries.get(0).getResource();
assertEquals("AND", o.getName().getCoding().get(0).getDisplay().getValue());
assertEquals("OR", o.getName().getCoding().get(1).getDisplay().getValue());
assertEquals("l1", o.getName().getCoding().get(1).getSystem().getValueAsString());
assertEquals("r1", o.getName().getCoding().get(1).getCode().getValue());
assertEquals("OR", o.getName().getCoding().get(2).getDisplay().getValue());
assertEquals("l2", o.getName().getCoding().get(2).getSystem().getValueAsString());
assertEquals("r2", o.getName().getCoding().get(2).getCode().getValue());
assertEquals("AND", o.getName().getCoding().get(3).getDisplay().getValue());
assertEquals("OR", o.getName().getCoding().get(4).getDisplay().getValue());
assertEquals("l3", o.getName().getCoding().get(4).getSystem().getValueAsString());
assertEquals("r3", o.getName().getCoding().get(4).getCode().getValue());
assertEquals("OR", o.getName().getCoding().get(5).getDisplay().getValue());
assertEquals("l4", o.getName().getCoding().get(5).getSystem().getValueAsString());
assertEquals("r4", o.getName().getCoding().get(5).getCode().getValue());
}
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
DummyObservationResourceProvider patientProvider = new DummyObservationResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
ourCtx = servlet.getFhirContext();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
ourCtx = servlet.getFhirContext();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyObservationResourceProvider implements IResourceProvider {
//@formatter:off
@Search
public List<Observation> findObservation(
@RequiredParam(name = Observation.SP_NAME_VALUE_DATE, compositeTypes= { StringParam.class, DateParam.class })
CompositeParam<StringParam, DateParam> theParam
) {
//@formatter:on
ArrayList<Observation> retVal = new ArrayList<Observation>();
Observation p = new Observation();
p.setId("1");
p.setApplies(theParam.getRightValue().getValueAsDateTimeDt());
p.getName().setText(theParam.getLeftValue().getValueAsStringDt());
retVal.add(p);
return retVal;
}
//@formatter:off
@Search
public List<Observation> findObservationNVS(
@RequiredParam(name = Observation.SP_NAME_VALUE_STRING, compositeTypes= { StringParam.class, StringParam.class })
CompositeAndListParam<StringParam, StringParam> theParam
) {
//@formatter:on
ArrayList<Observation> retVal = new ArrayList<Observation>();
Observation p = new Observation();
p.setId("1");
for (CompositeOrListParam<StringParam, StringParam> nextAnd : theParam.getValuesAsQueryTokens()) {
p.getName().addCoding().getDisplay().setValue("AND");
for (CompositeParam<StringParam, StringParam> nextOr : nextAnd.getValuesAsQueryTokens()) {
p.getName().addCoding().setDisplay("OR").setSystem(nextOr.getLeftValue().getValue()).setCode(nextOr.getRightValue().getValue());
}
}
retVal.add(p);
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Observation.class;
}
}
}

View File

@ -2,8 +2,6 @@ package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -15,9 +13,10 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -42,89 +41,103 @@ import ca.uhn.fhir.testutil.RandomServerPortProvider;
public class ResfulServerSelfReferenceTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResfulServerSelfReferenceTest.class);
private static int ourPort;
private static Server ourServer;
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx;
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
ourCtx = new FhirContext(Patient.class);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServerProfileProvider profProvider=new ServerProfileProvider(ourCtx);
ServletHandler proxyHandler = new ServletHandler();
ServletHolder servletHolder = new ServletHolder(new DummyRestfulServer(patientProvider,profProvider));
proxyHandler.addServletWithMapping(servletHolder, "/fhir/context/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@Test
public void testContextWithSpace() throws Exception {
int port = RandomServerPortProvider.findFreePort();
Server server = new Server(port);
RestfulServer restServer = new RestfulServer();
restServer.setFhirContext(ourCtx);
restServer.setResourceProviders(new DummyPatientResourceProvider());
// ServletHandler proxyHandler = new ServletHandler();
ServletHolder servletHolder = new ServletHolder(restServer);
ServletContextHandler ch = new ServletContextHandler();
ch.setContextPath("/root ctx/rcp2");
ch.addServlet(servletHolder, "/fhir ctx/fcp2/*");
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
server.setHandler(ch);
server.start();
try {
String baseUri = "http://localhost:" + port + "/root%20ctx/rcp2/fhir%20ctx/fcp2";
String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001";
HttpGet httpGet = new HttpGet(uri);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
} finally {
server.stop();
}
}
@Test
public void testSearchByParamIdentifier() throws Exception {
int port = RandomServerPortProvider.findFreePort();
Server hServer = new Server(port);
String baseUri = "http://localhost:" + ourPort + "/fhir/context";
String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001";
HttpGet httpGet = new HttpGet(uri);
HttpResponse status = ourClient.execute(httpGet);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServerProfileProvider profProvider = new ServerProfileProvider(ourCtx);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
ServletHandler proxyHandler = new ServletHandler();
RestfulServer server = new RestfulServer();
server.setFhirContext(ourCtx);
server.setResourceProviders(patientProvider, profProvider);
ServletHolder servletHolder = new ServletHolder(server);
proxyHandler.addServletWithMapping(servletHolder, "/fhir/context/*");
hServer.setHandler(proxyHandler);
hServer.start();
try {
String baseUri = "http://localhost:" + port + "/fhir/context";
String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001";
HttpGet httpGet = new HttpGet(uri);
HttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(1, bundle.getEntries().size());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
Patient patient = (Patient) bundle.getEntries().get(0).getResource();
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
assertEquals(1, bundle.getEntries().size());
assertEquals(uri, bundle.getLinkSelf().getValue());
assertEquals(baseUri, bundle.getLinkBase().getValue());
Patient patient = (Patient) bundle.getEntries().get(0).getResource();
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
assertEquals(uri, bundle.getLinkSelf().getValue());
assertEquals(baseUri, bundle.getLinkBase().getValue());
} finally {
hServer.stop();
}
}
public static class DummyRestfulServer extends RestfulServer {
private static final long serialVersionUID = 1L;
private Collection<IResourceProvider> myResourceProviders;
public DummyRestfulServer(IResourceProvider... theResourceProviders) {
myResourceProviders = Arrays.asList(theResourceProviders);
}
@Override
public Collection<IResourceProvider> getResourceProviders() {
return myResourceProviders;
}
@Override
public ISecurityManager getSecurityManager() {
return null;
}
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -161,8 +174,6 @@ public class ResfulServerSelfReferenceTest {
return idToPatient;
}
@Search()
public Patient getPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
for (Patient next : getIdToPatient().values()) {
@ -174,8 +185,7 @@ public class ResfulServerSelfReferenceTest {
}
return null;
}
/**
* Retrieve the resource by its identifier
*
@ -195,6 +205,4 @@ public class ResfulServerSelfReferenceTest {
}
}

View File

@ -13,6 +13,7 @@ import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortSpec;
public class SearchParameterMap extends HashMap<String, List<List<? extends IQueryParameterType>>> {
@ -20,6 +21,8 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
private Set<Include> myIncludes;
private SortSpec mySort;
public void add(String theName, IQueryParameterAnd<?> theAnd) {
if (theAnd == null) {
return;
@ -59,6 +62,10 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
get(theName).add(list);
}
public void addInclude(Include theInclude) {
getIncludes().add(theInclude);
}
public Set<Include> getIncludes() {
if (myIncludes == null) {
myIncludes = new HashSet<Include>();
@ -66,12 +73,16 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
return myIncludes;
}
public SortSpec getSort() {
return mySort;
}
public void setIncludes(Set<Include> theIncludes) {
myIncludes = theIncludes;
}
public void addInclude(Include theInclude) {
getIncludes().add(theInclude);
public void setSort(SortSpec theSort) {
mySort = theSort;
}
@Override

View File

@ -65,7 +65,7 @@
<property name="persistenceUnitName" value="FHIR_UT" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />

View File

@ -31,34 +31,34 @@ function generateHapiSearch(json, container) {
var nextParam = json.params[i];
var paramLine = null;
if (nextParam.type == 'string') {
paramLine = '.where(new StringParam("' + nextParam.name + '")';
paramLine = '.where(new StringClientParam("' + nextParam.name + '")';
paramLine += nextParam.qualifier = ':exact' ? '.matchesExactly()' : '.matches()';
paramLine += '.value("' + nextParam.value + '"))';
} else if (nextParam.type == 'token') {
var idx = nextParam.value.indexOf('|');
if (idx == -1) {
paramLine = '.where(new TokenParam("' + nextParam.name + '").exactly().code("' + nextParam.value + '"))';
paramLine = '.where(new TokenClientParam("' + nextParam.name + '").exactly().code("' + nextParam.value + '"))';
} else {
paramLine = '.where(new TokenParam("' + nextParam.name + '").exactly().systemAndCode("' + nextParam.value.substring(0,idx) + '", "' + nextParam.value.substring(idx+1) + '"))';
paramLine = '.where(new TokenClientParam("' + nextParam.name + '").exactly().systemAndCode("' + nextParam.value.substring(0,idx) + '", "' + nextParam.value.substring(idx+1) + '"))';
}
} else if (nextParam.type == 'number') {
paramLine = '.where(new NumberParam("' + nextParam.name + '").exactly().value("' + nextParam.value + '"))';
paramLine = '.where(new NumberClientParam("' + nextParam.name + '").exactly().value("' + nextParam.value + '"))';
} else if (nextParam.type == 'reference') {
if (nextParam.qualifier == '') {
if (nextParam.name.indexOf('.') == -1) {
paramLine = '.where(new ReferenceParam("' + nextParam.name + '").hasId("' + nextParam.value + '"))';
paramLine = '.where(new ReferenceClientParam("' + nextParam.name + '").hasId("' + nextParam.value + '"))';
}
}
} else if (nextParam.type == 'date') {
var dateQual = nextParam.value.indexOf('T') == -1 ? 'day' : 'second';
if (nextParam.value.substring(0,2) == '>=') {
paramLine = '.where(new DateParam("' + nextParam.name + '").afterOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
paramLine = '.where(new DateClientParam("' + nextParam.name + '").afterOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
} else if (nextParam.value.substring(0,1) == '>') {
paramLine = '.where(new DateParam("' + nextParam.name + '").after().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
paramLine = '.where(new DateClientParam("' + nextParam.name + '").after().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
} else if (nextParam.value.substring(0,2) == '<=') {
paramLine = '.where(new DateParam("' + nextParam.name + '").beforeOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
paramLine = '.where(new DateClientParam("' + nextParam.name + '").beforeOrEquals().' + dateQual + '("' + nextParam.value.substring(2) + '"))';
} else if (nextParam.value.substring(0,1) == '<') {
paramLine = '.where(new DateParam("' + nextParam.name + '").before().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
paramLine = '.where(new DateClientParam("' + nextParam.name + '").before().' + dateQual + '("' + nextParam.value.substring(1) + '"))';
}
}
if (paramLine != null) {

View File

@ -1,17 +1,12 @@
package ca.uhn.fhir.tinder;
import static org.apache.commons.lang.StringUtils.defaultString;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.ParseException;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
@ -25,8 +20,6 @@ import org.apache.maven.project.MavenProject;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import ca.uhn.fhir.tinder.model.BaseRootType;
import ca.uhn.fhir.tinder.model.Extension;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
@Mojo(name = "generate-jparest-server", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
@ -151,7 +144,7 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
TinderJpaRestServerMojo mojo = new TinderJpaRestServerMojo();
mojo.packageBase = "ca.uhn.test";
mojo.baseResourceNames = java.util.Collections.singletonList("observation");
mojo.targetDirectory = new File("target/gen");
mojo.targetDirectory = new File("target/generated/valuesets");
mojo.execute();
}

View File

@ -15,6 +15,7 @@ public class SearchParameter {
private List<String> myTargetTypes;
private String myType;
private List<String> myCompositeOf;
private List<String> myCompositeTypes;
public SearchParameter() {
@ -124,4 +125,15 @@ public class SearchParameter {
return myCompositeOf;
}
public void setCompositeTypes(List<String> theCompositeTypes) {
myCompositeTypes = theCompositeTypes;
}
public List<String> getCompositeTypes() {
if (myCompositeTypes == null) {
myCompositeTypes = new ArrayList<String>();
}
return myCompositeTypes;
}
}

View File

@ -14,13 +14,12 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.tinder.model.AnyChild;
import ca.uhn.fhir.tinder.model.BaseElement;
import ca.uhn.fhir.tinder.model.BaseRootType;
@ -261,7 +260,8 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
composite.setDescription(nextCompositeParam.getDescription());
composite.setPath(nextCompositeParam.getPath());
composite.setType("composite");
composite.setCompositeOf(Arrays.asList(part1.getPath(), part2.getPath()));
composite.setCompositeOf(Arrays.asList(part1.getName(), part2.getName()));
composite.setCompositeTypes(Arrays.asList(WordUtils.capitalize(part1.getType()), WordUtils.capitalize(part2.getType())));
}
}

View File

@ -29,7 +29,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
@Description(shortDefinition="The resource identity")
@OptionalParam(name="_id")
StringParam theId,
#foreach ( $param in $searchParamsWithoutComposite ) #{if}(true) #{end}
#foreach ( $param in $searchParams ) #{if}(true) #{end}
@Description(shortDefinition="${param.description}")
#if (${param.type} == 'string' )
@ -43,16 +43,16 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
DateRangeParam the${param.nameCapitalized},
#elseif (${param.type} == 'quantity' )
@OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized},
QuantityAndListParam the${param.nameCapitalized},
#elseif (${param.type} == 'number' )
@OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized},
NumberAndListParam the${param.nameCapitalized},
#elseif (${param.type} == 'reference' )
@OptionalParam(name="${param.name}", targetTypes={ #{foreach}($nextType in ${param.targetTypes}) ${nextType}.class #{if}($foreach.hasNext), #{end} #{end} } )
ReferenceParam the${param.nameCapitalized},
ReferenceAndListParam the${param.nameCapitalized},
#elseif (${param.type} == 'composite' )
@OptionalParam(name="${param.name}")
ReferenceParam the${param.nameCapitalized},
@OptionalParam(name="${param.name}", compositeTypes= { ${param.compositeTypes[0]}Param.class, ${param.compositeTypes[1]}Param.class })
CompositeAndListParam<${param.compositeTypes[0]}Param, ${param.compositeTypes[1]}Param> the${param.nameCapitalized},
#end
#end
@ -71,7 +71,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("_id", theId);
#foreach ( $param in $searchParamsWithoutComposite )
#foreach ( $param in $searchParams )
paramMap.add("${param.name}", the${param.nameCapitalized});
#end

View File

@ -60,7 +60,11 @@ public class ${className} extends BaseResource implements IResource {
* Path: <b>${param.path}</b><br/>
* </p>
*/
#if( ${param.typeCapitalized} == 'Composite' )
public static final CompositeClientParam<${param.compositeTypes[0]}ClientParam, ${param.compositeTypes[1]}ClientParam> ${param.fluentConstantName} = new CompositeClientParam<${param.compositeTypes[0]}ClientParam, ${param.compositeTypes[1]}ClientParam>(${param.constantName});
#else
public static final ${param.typeCapitalized}ClientParam ${param.fluentConstantName} = new ${param.typeCapitalized}ClientParam(${param.constantName});
#end
#if( ${param.typeCapitalized} == 'Reference' )
#foreach ( $include in $param.paths )

View File

@ -6,6 +6,12 @@
<dependent-module archiveName="hapi-fhir-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type>
</dependent-module>
<property name="context-root" value="restful-server-example"/>
<property name="java-output-path" value="/restful-server-example/target/classes"/>
</wb-module>

View File

@ -3,7 +3,7 @@
<groupId>ca.uhn.hapi.example</groupId>
<artifactId>restful-server-example</artifactId>
<version>0.3</version>
<version>0.5-SNAPSHOT</version>
<packaging>war</packaging>
<name>Sample RESTful Server (HAPI-FHIR)</name>
@ -17,6 +17,15 @@
<version>0.5-SNAPSHOT</version>
</dependency>
<!-- This dependency is used for the "test page" web app overlay -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>0.5-SNAPSHOT</version>
<type>war</type>
<scope>provided</scope>
</dependency>
<!-- HAPI-FHIR uses Logback for logging support. The logback library is included automatically by Maven as a part of the hapi-fhir-base dependency, but you also need to include a
logging library. Logback is used here, but log4j would also be fine. -->
<dependency>
@ -48,6 +57,9 @@
<build>
<plugins>
<!--
Tell Maven which Java source version you want to use
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@ -57,13 +69,33 @@
<target>1.6</target>
</configuration>
</plugin>
<!--
The configuration here tells the WAR plugin to include the test
page overlay. You can omit it if you are not using that feature.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<overlays>
<overlay>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
<!--
This plugin is just a part of the HAPI internal build process, you do not
need to incude it in your own projects
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
<skip>false</skip>
</configuration>
</plugin>