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"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="bin" path="src/site/example/java"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>

View File

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

View File

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

View File

@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; 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.ICompositeElement;
import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement; 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.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.IResourceBlock; import ca.uhn.fhir.model.api.IResourceBlock;
@ -299,8 +299,6 @@ class ModelScanner {
scanCompositeElementForChildren(theClass, resourceDef); scanCompositeElementForChildren(theClass, resourceDef);
} }
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) { private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement return null; // TODO: implement
} }
@ -602,6 +600,9 @@ class ModelScanner {
private void scanResourceForSearchParams(Class<? extends IResource> theClass, RuntimeResourceDefinition theResourceDef) { 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()) { for (Field nextField : theClass.getFields()) {
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class); SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
if (searchParam != null) { if (searchParam != null) {
@ -609,11 +610,34 @@ class ModelScanner {
if (paramType == null) { if (paramType == null) {
throw new ConfigurationException("Searc param " + searchParam.name() + " has an invalid type: " + searchParam.type()); 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); RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType);
theResourceDef.addSearchParam(param); 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() { public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {

View File

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

View File

@ -16,26 +16,6 @@
package ca.uhn.fhir.model.dstu.resource; 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.Date;
import java.util.List; 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.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.composite.SampledDataDt; import ca.uhn.fhir.model.dstu.composite.SampledDataDt;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum; 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.ObservationStatusEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; 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.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
@ -113,7 +88,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.name</b><br/> * Path: <b>Observation.name</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.value[x]</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.value[x]</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.value[x]</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.value[x]</b><br/>
* </p> * </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"; 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); 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> * Search parameter constant for <b>date</b>
* <p> * <p>
@ -239,7 +193,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.applies[x]</b><br/> * Path: <b>Observation.applies[x]</b><br/>
* </p> * </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"; public static final String SP_DATE = "date";
/** /**
@ -260,7 +214,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.status</b><br/> * Path: <b>Observation.status</b><br/>
* </p> * </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"; public static final String SP_STATUS = "status";
/** /**
@ -281,7 +235,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.reliability</b><br/> * Path: <b>Observation.reliability</b><br/>
* </p> * </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"; public static final String SP_RELIABILITY = "reliability";
/** /**
@ -302,7 +256,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.subject</b><br/> * Path: <b>Observation.subject</b><br/>
* </p> * </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"; public static final String SP_SUBJECT = "subject";
/** /**
@ -329,7 +283,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.performer</b><br/> * Path: <b>Observation.performer</b><br/>
* </p> * </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"; public static final String SP_PERFORMER = "performer";
/** /**
@ -356,7 +310,7 @@ public class Observation extends BaseResource implements IResource {
* Path: <b>Observation.specimen</b><br/> * Path: <b>Observation.specimen</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.related.type</b><br/>
* </p> * </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"; 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/> * Path: <b>Observation.related.target</b><br/>
* </p> * </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"; 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"); 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> * <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/> * Type: <b>composite</b><br/>
* Path: <b>related-target & related-type</b><br/> * Path: <b>name & value-[x]</b><br/>
* </p> * </p>
*/ */
@SearchParamDefinition(name="related", path="related-target & related-type", description="Related Observations - search on related-type and related-target together", type="composite") @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_RELATED = "related"; 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> * <p>
* Description: <b>Related Observations - search on related-type and related-target together</b><br/> * Description: <b>Related Observations - search on related-type and related-target together</b><br/>
* Type: <b>composite</b><br/> * Type: <b>composite</b><br/>
* Path: <b>related-target & related-type</b><br/> * Path: <b>related-target & related-type</b><br/>
* </p> * </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) @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.", shortDefinition="High, low, normal, etc.",
formalDefinition="The assessment made based on the result of the observation." 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) @Child(name="comments", type=StringDt.class, order=3, min=0, max=1)
@Description( @Description(
@ -494,14 +532,14 @@ public class Observation extends BaseResource implements IResource {
shortDefinition="registered | preliminary | final | amended +", shortDefinition="registered | preliminary | final | amended +",
formalDefinition="The status of the result value" 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) @Child(name="reliability", type=CodeDt.class, order=7, min=1, max=1)
@Description( @Description(
shortDefinition="ok | ongoing | early | questionable | calibrating | error +", shortDefinition="ok | ongoing | early | questionable | calibrating | error +",
formalDefinition="An estimate of the degree to which quality issues have impacted on the value reported" 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) @Child(name="bodySite", type=CodeableConceptDt.class, order=8, min=0, max=1)
@Description( @Description(
@ -642,9 +680,9 @@ public class Observation extends BaseResource implements IResource {
* The assessment made based on the result of the observation. * The assessment made based on the result of the observation.
* </p> * </p>
*/ */
public BoundCodeableConceptDt<ObservationInterpretationCodesEnum> getInterpretation() { public CodeableConceptDt getInterpretation() {
if (myInterpretation == null) { if (myInterpretation == null) {
myInterpretation = new BoundCodeableConceptDt<ObservationInterpretationCodesEnum>(ObservationInterpretationCodesEnum.VALUESET_BINDER); myInterpretation = new CodeableConceptDt();
} }
return myInterpretation; return myInterpretation;
} }
@ -657,24 +695,11 @@ public class Observation extends BaseResource implements IResource {
* The assessment made based on the result of the observation. * The assessment made based on the result of the observation.
* </p> * </p>
*/ */
public Observation setInterpretation(BoundCodeableConceptDt<ObservationInterpretationCodesEnum> theValue) { public Observation setInterpretation(CodeableConceptDt theValue) {
myInterpretation = theValue; myInterpretation = theValue;
return this; 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). * Gets the value(s) for <b>comments</b> (Comments about result).
@ -786,8 +811,8 @@ public class Observation extends BaseResource implements IResource {
* *
* </p> * </p>
*/ */
public Observation setIssued( Date theDate, TemporalPrecisionEnum thePrecision) { public Observation setIssuedWithMillisPrecision( Date theDate) {
myIssued = new InstantDt(theDate, thePrecision); myIssued = new InstantDt(theDate);
return this; return this;
} }
@ -799,8 +824,8 @@ public class Observation extends BaseResource implements IResource {
* *
* </p> * </p>
*/ */
public Observation setIssuedWithMillisPrecision( Date theDate) { public Observation setIssued( Date theDate, TemporalPrecisionEnum thePrecision) {
myIssued = new InstantDt(theDate); myIssued = new InstantDt(theDate, thePrecision);
return this; return this;
} }
@ -815,9 +840,9 @@ public class Observation extends BaseResource implements IResource {
* The status of the result value * The status of the result value
* </p> * </p>
*/ */
public BoundCodeDt<ObservationStatusEnum> getStatus() { public CodeDt getStatus() {
if (myStatus == null) { if (myStatus == null) {
myStatus = new BoundCodeDt<ObservationStatusEnum>(ObservationStatusEnum.VALUESET_BINDER); myStatus = new CodeDt();
} }
return myStatus; return myStatus;
} }
@ -830,25 +855,25 @@ public class Observation extends BaseResource implements IResource {
* The status of the result value * The status of the result value
* </p> * </p>
*/ */
public Observation setStatus(BoundCodeDt<ObservationStatusEnum> theValue) { public Observation setStatus(CodeDt theValue) {
myStatus = theValue; myStatus = theValue;
return this; 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> * <p>
* <b>Definition:</b> * <b>Definition:</b>
* The status of the result value * The status of the result value
* </p> * </p>
*/ */
public Observation setStatus(ObservationStatusEnum theValue) { public Observation setStatus( String theCode) {
getStatus().setValueAsEnum(theValue); myStatus = new CodeDt(theCode);
return this; return this;
} }
/** /**
* Gets the value(s) for <b>reliability</b> (ok | ongoing | early | questionable | calibrating | error +). * Gets the value(s) for <b>reliability</b> (ok | ongoing | early | questionable | calibrating | error +).
* creating it if it does * 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 * An estimate of the degree to which quality issues have impacted on the value reported
* </p> * </p>
*/ */
public BoundCodeDt<ObservationReliabilityEnum> getReliability() { public CodeDt getReliability() {
if (myReliability == null) { if (myReliability == null) {
myReliability = new BoundCodeDt<ObservationReliabilityEnum>(ObservationReliabilityEnum.VALUESET_BINDER); myReliability = new CodeDt();
} }
return myReliability; 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 * An estimate of the degree to which quality issues have impacted on the value reported
* </p> * </p>
*/ */
public Observation setReliability(BoundCodeDt<ObservationReliabilityEnum> theValue) { public Observation setReliability(CodeDt theValue) {
myReliability = theValue; myReliability = theValue;
return this; 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> * <p>
* <b>Definition:</b> * <b>Definition:</b>
* An estimate of the degree to which quality issues have impacted on the value reported * An estimate of the degree to which quality issues have impacted on the value reported
* </p> * </p>
*/ */
public Observation setReliability(ObservationReliabilityEnum theValue) { public Observation setReliability( String theCode) {
getReliability().setValueAsEnum(theValue); myReliability = new CodeDt(theCode);
return this; return this;
} }
/** /**
* Gets the value(s) for <b>bodySite</b> (Observed body part). * Gets the value(s) for <b>bodySite</b> (Observed body part).
* creating it if it does * creating it if it does
@ -993,8 +1018,8 @@ public class Observation extends BaseResource implements IResource {
* A unique identifier for the simple observation * A unique identifier for the simple observation
* </p> * </p>
*/ */
public Observation setIdentifier( IdentifierUseEnum theUse, String theSystem, String theValue, String theLabel) { public Observation setIdentifier( String theSystem, String theValue) {
myIdentifier = new IdentifierDt(theUse, theSystem, theValue, theLabel); myIdentifier = new IdentifierDt(theSystem, theValue);
return this; return this;
} }
@ -1006,8 +1031,8 @@ public class Observation extends BaseResource implements IResource {
* A unique identifier for the simple observation * A unique identifier for the simple observation
* </p> * </p>
*/ */
public Observation setIdentifier( String theSystem, String theValue) { public Observation setIdentifier( IdentifierUseEnum theUse, String theSystem, String theValue, String theLabel) {
myIdentifier = new IdentifierDt(theSystem, theValue); myIdentifier = new IdentifierDt(theUse, theSystem, theValue, theLabel);
return this; return this;
} }
@ -1324,32 +1349,6 @@ public class Observation extends BaseResource implements IResource {
* <p> * <p>
* <b>Definition:</b> * <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 * 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> * </p>
*/ */
public ReferenceRange setLow( QuantityCompararatorEnum theComparator, double theValue, String theUnits) { public ReferenceRange setLow( QuantityCompararatorEnum theComparator, double theValue, String theUnits) {
@ -1363,19 +1362,6 @@ public class Observation extends BaseResource implements IResource {
* <p> * <p>
* <b>Definition:</b> * <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 * 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> * </p>
*/ */
public ReferenceRange setLow( double theValue) { public ReferenceRange setLow( double theValue) {
@ -1396,6 +1382,45 @@ public class Observation extends BaseResource implements IResource {
return this; 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). * Gets the value(s) for <b>high</b> (High Range, if relevant).
@ -1433,32 +1458,6 @@ public class Observation extends BaseResource implements IResource {
* <p> * <p>
* <b>Definition:</b> * <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 * 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> * </p>
*/ */
public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, double theValue, String theUnits) { public ReferenceRange setHigh( QuantityCompararatorEnum theComparator, double theValue, String theUnits) {
@ -1472,19 +1471,6 @@ public class Observation extends BaseResource implements IResource {
* <p> * <p>
* <b>Definition:</b> * <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 * 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> * </p>
*/ */
public ReferenceRange setHigh( double theValue) { public ReferenceRange setHigh( double theValue) {
@ -1505,6 +1491,45 @@ public class Observation extends BaseResource implements IResource {
return this; 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). * 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", 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" 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={ @Child(name="target", order=1, min=1, max=1, type={
ca.uhn.fhir.model.dstu.resource.Observation.class }) 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 * A code specifying the kind of relationship that exists with the target observation
* </p> * </p>
*/ */
public BoundCodeDt<ObservationRelationshipTypeEnum> getType() { public CodeDt getType() {
if (myType == null) { if (myType == null) {
myType = new BoundCodeDt<ObservationRelationshipTypeEnum>(ObservationRelationshipTypeEnum.VALUESET_BINDER); myType = new CodeDt();
} }
return myType; 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 * A code specifying the kind of relationship that exists with the target observation
* </p> * </p>
*/ */
public Related setType(BoundCodeDt<ObservationRelationshipTypeEnum> theValue) { public Related setType(CodeDt theValue) {
myType = theValue; myType = theValue;
return this; 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> * <p>
* <b>Definition:</b> * <b>Definition:</b>
* A code specifying the kind of relationship that exists with the target observation * A code specifying the kind of relationship that exists with the target observation
* </p> * </p>
*/ */
public Related setType(ObservationRelationshipTypeEnum theValue) { public Related setType( String theCode) {
getType().setValueAsEnum(theValue); myType = new CodeDt(theCode);
return this; return this;
} }
/** /**
* Gets the value(s) for <b>target</b> (Observation that is related to this one). * Gets the value(s) for <b>target</b> (Observation that is related to this one).
* creating it if it does * 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; 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> * <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 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any
* limit of 36 characters. * other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -52,7 +53,7 @@ public class IdDt extends BasePrimitive<String> {
private String myResourceType; private String myResourceType;
private String myUnqualifiedId; private String myUnqualifiedId;
private String myUnqualifiedVersionId; private String myUnqualifiedVersionId;
private String myValue; private volatile String myValue;
/** /**
* Create a new empty ID * 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) { public IdDt(BigDecimal thePid) {
if (thePid != null) { 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> * <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 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* limit of 36 characters. * any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -107,13 +110,6 @@ public class IdDt extends BasePrimitive<String> {
this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); 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 * 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() { public BigDecimal asBigDecimal() {
return getIdPartAsBigDecimal(); 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 @Override
public IdDt getId() { 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() * @see #getIdPart()
*/ */
@Override @Override
public String getValue() { public String getValue() {
if (myValue == null && myHaveComponentParts) { if (myValue == null && myHaveComponentParts) {
if (myUnqualifiedVersionId != null) { StringBuilder b = new StringBuilder();
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId; if (isNotBlank(myResourceType)) {
} else { b.append(myResourceType);
myValue = myResourceType + '/' + myUnqualifiedId; 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; 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() { public boolean isIdPartValidLong() {
String id = getIdPart(); 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 @Override
public void setId(IdDt theId) { public void setId(IdDt theId) {
@ -289,8 +298,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value * Set the value
* *
* <p> * <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 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* limit of 36 characters. * any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -300,7 +309,7 @@ public class IdDt extends BasePrimitive<String> {
public void setValue(String theValue) throws DataFormatException { public void setValue(String theValue) throws DataFormatException {
// TODO: add validation // TODO: add validation
myValue = theValue; myValue = theValue;
myHaveComponentParts=false; myHaveComponentParts = false;
if (StringUtils.isBlank(theValue)) { if (StringUtils.isBlank(theValue)) {
myValue = null; myValue = null;
myUnqualifiedId = null; myUnqualifiedId = null;
@ -337,8 +346,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value * Set the value
* *
* <p> * <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 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* limit of 36 characters. * any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * 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, * Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be
* 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. * 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 * @param theServerBase
* The server base (e.g. "http://example.com/fhir") * The server base (e.g. "http://example.com/fhir")
@ -398,29 +413,31 @@ public class IdDt extends BasePrimitive<String> {
} }
retVal.append('/'); retVal.append('/');
retVal.append(getIdPart()); retVal.append(getIdPart());
if (hasVersionIdPart()) { if (hasVersionIdPart()) {
retVal.append('/'); retVal.append('/');
retVal.append(Constants.PARAM_HISTORY); retVal.append(Constants.PARAM_HISTORY);
retVal.append('/'); retVal.append('/');
retVal.append(getVersionIdPart()); retVal.append(getVersionIdPart());
} }
return retVal.toString(); 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 * @param theVersion
* The actual version string, e.g. "1" * 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) { public IdDt withVersion(String theVersion) {
Validate.notBlank(theVersion, "Version may not be null or empty"); Validate.notBlank(theVersion, "Version may not be null or empty");
String existingValue = getValue(); String existingValue = getValue();
int i = existingValue.indexOf(Constants.PARAM_HISTORY); int i = existingValue.indexOf(Constants.PARAM_HISTORY);
String value; String value;
if (i > 1) { if (i > 1) {
@ -432,8 +449,11 @@ public class IdDt extends BasePrimitive<String> {
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion); return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
} }
public IdDt withResourceType(String theResourceName) { private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
return new IdDt(theResourceName, getIdPart(), getVersionIdPart()); 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()); 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 * 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; 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; import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
/**
* Parameter annotation which specifies a search parameter for a {@link Search} method.
*/
public @interface OptionalParam { 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 * 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 {}; 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; 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; import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
/**
* Parameter annotation which specifies a search parameter for a {@link Search} method.
*/
public @interface RequiredParam { 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 * 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 {}; 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.ITransaction;
import ca.uhn.fhir.rest.gclient.ITransactionTyped; import ca.uhn.fhir.rest.gclient.ITransactionTyped;
import ca.uhn.fhir.rest.gclient.IUntypedQuery; 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.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding; import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation; 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.ReadMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding; import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.TransactionMethodBinding; 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.method.ValidateMethodBinding;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -278,7 +279,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override @Override
public MethodOutcome update(IdDt theIdDt, IResource theResource) { 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()) { if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
@ -344,6 +345,24 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this; 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") @SuppressWarnings("unchecked")
@Override @Override
public T encodedJson() { public T encodedJson() {
@ -419,23 +438,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override @Override
public MethodOutcome execute() { public MethodOutcome execute() {
if (myResource == null) { if (myResource == null) {
EncodingEnum encoding = null; myResource = parseResourceBody(myResourceBody);
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);
} }
myId = getPreferredId(myResource, myId);
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext); BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
@ -448,6 +454,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public ICreateTyped resource(IResource theResource) { public ICreateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null"); 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 { 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.IGetTags;
import ca.uhn.fhir.rest.gclient.ITransaction; import ca.uhn.fhir.rest.gclient.ITransaction;
import ca.uhn.fhir.rest.gclient.IUntypedQuery; import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
public interface IGenericClient { public interface IGenericClient {
/** /**
@ -268,4 +269,9 @@ public interface IGenericClient {
*/ */
IDelete delete(); 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! * Composite parameter type for use in fluent client interfaces
*
* TODO: implement
*/ */
public class CompositeClientParam { public class CompositeClientParam<A extends IParam, B extends IParam> implements IParam {
private String myName;
public CompositeClientParam(String theName) { public CompositeClientParam(String theName) {
// TODO Auto-generated constructor stub myName=theName;
} }
/**
* Composite params are not yet supported - This is a placeholder. Help welcome! @Override
* public String getParamName() {
* TODO: implement return myName;
*/
public void notYetSupported() {
//nothig
} }
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. * 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) { public CompositeParam(String theParamName) {
super(theParamName); super(theParamName);

View File

@ -61,7 +61,7 @@ public class DateClientParam implements IParam {
return new DateWithPrefix(""); return new DateWithPrefix("");
} }
private class Criterion implements ICriterion, ICriterionInternal { private class Criterion implements ICriterion<DateClientParam>, ICriterionInternal {
private String myValue; private String myValue;
@ -89,35 +89,35 @@ public class DateClientParam implements IParam {
} }
@Override @Override
public ICriterion day(Date theValue) { public ICriterion<DateClientParam> day(Date theValue) {
DateTimeDt dt = new DateTimeDt(theValue); DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY); dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString()); return new Criterion(myPrefix + dt.getValueAsString());
} }
@Override @Override
public ICriterion day(String theValue) { public ICriterion<DateClientParam> day(String theValue) {
DateTimeDt dt = new DateTimeDt(theValue); DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY); dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString()); return new Criterion(myPrefix + dt.getValueAsString());
} }
@Override @Override
public ICriterion now() { public ICriterion<DateClientParam> now() {
DateTimeDt dt = new DateTimeDt(); DateTimeDt dt = new DateTimeDt();
dt.setPrecision(TemporalPrecisionEnum.DAY); dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString()); return new Criterion(myPrefix + dt.getValueAsString());
} }
@Override @Override
public ICriterion second(Date theValue) { public ICriterion<DateClientParam> second(Date theValue) {
DateTimeDt dt = new DateTimeDt(theValue); DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY); dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString()); return new Criterion(myPrefix + dt.getValueAsString());
} }
@Override @Override
public ICriterion second(String theValue) { public ICriterion<DateClientParam> second(String theValue) {
DateTimeDt dt = new DateTimeDt(theValue); DateTimeDt dt = new DateTimeDt(theValue);
dt.setPrecision(TemporalPrecisionEnum.DAY); dt.setPrecision(TemporalPrecisionEnum.DAY);
return new Criterion(myPrefix + dt.getValueAsString()); return new Criterion(myPrefix + dt.getValueAsString());
@ -127,15 +127,15 @@ public class DateClientParam implements IParam {
public interface IDateSpecifier { 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% * #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> { 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); 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; myParamName = theParamName;
} }
public IMatches<ICriterion> exactly() { public IMatches<ICriterion<NumberClientParam>> exactly() {
return new IMatches<ICriterion>() { return new IMatches<ICriterion<NumberClientParam>>() {
@Override @Override
public ICriterion number(long theNumber) { public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion(getParamName(), Long.toString(theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), Long.toString(theNumber));
} }
@Override @Override
public ICriterion number(String theNumber) { public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion(getParamName(), (theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), (theNumber));
} }
}; };
} }
@ -50,58 +50,58 @@ public class NumberClientParam implements IParam {
return myParamName; return myParamName;
} }
public IMatches<ICriterion> greaterThan() { public IMatches<ICriterion<NumberClientParam>> greaterThan() {
return new IMatches<ICriterion>() { return new IMatches<ICriterion<NumberClientParam>>() {
@Override @Override
public ICriterion number(long theNumber) { public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion(getParamName(), ">" + Long.toString(theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), ">" + Long.toString(theNumber));
} }
@Override @Override
public ICriterion number(String theNumber) { public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion(getParamName(), ">" + (theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), ">" + (theNumber));
} }
}; };
} }
public IMatches<ICriterion> greaterThanOrEqual() { public IMatches<ICriterion<NumberClientParam>> greaterThanOrEqual() {
return new IMatches<ICriterion>() { return new IMatches<ICriterion<NumberClientParam>>() {
@Override @Override
public ICriterion number(long theNumber) { public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion(getParamName(), ">=" + Long.toString(theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), ">=" + Long.toString(theNumber));
} }
@Override @Override
public ICriterion number(String theNumber) { public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion(getParamName(), ">=" + (theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), ">=" + (theNumber));
} }
}; };
} }
public IMatches<ICriterion> lessThan() { public IMatches<ICriterion<NumberClientParam>> lessThan() {
return new IMatches<ICriterion>() { return new IMatches<ICriterion<NumberClientParam>>() {
@Override @Override
public ICriterion number(long theNumber) { public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion(getParamName(), "<" + Long.toString(theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), "<" + Long.toString(theNumber));
} }
@Override @Override
public ICriterion number(String theNumber) { public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion(getParamName(), "<" + (theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), "<" + (theNumber));
} }
}; };
} }
public IMatches<ICriterion> lessThanOrEqual() { public IMatches<ICriterion<NumberClientParam>> lessThanOrEqual() {
return new IMatches<ICriterion>() { return new IMatches<ICriterion<NumberClientParam>>() {
@Override @Override
public ICriterion number(long theNumber) { public ICriterion<NumberClientParam> number(long theNumber) {
return new StringCriterion(getParamName(), "<=" + Long.toString(theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), "<=" + Long.toString(theNumber));
} }
@Override @Override
public ICriterion number(String theNumber) { public ICriterion<NumberClientParam> number(String theNumber) {
return new StringCriterion(getParamName(), "<=" + (theNumber)); return new StringCriterion<NumberClientParam>(getParamName(), "<=" + (theNumber));
} }
}; };
} }

View File

@ -141,11 +141,11 @@ public class QuantityClientParam implements IParam {
public interface IAndUnits { 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 { private class AndUnits implements IAndUnits {
@ -157,18 +157,18 @@ public class QuantityClientParam implements IParam {
} }
@Override @Override
public ICriterion andNoUnits() { public ICriterion<QuantityClientParam> andNoUnits() {
return andUnits(null, null); return andUnits(null, null);
} }
@Override @Override
public ICriterion andUnits(String theUnits) { public ICriterion<QuantityClientParam> andUnits(String theUnits) {
return andUnits(theUnits, null); return andUnits(theUnits, null);
} }
@Override @Override
public ICriterion andUnits(String theSystem, String theUnits) { public ICriterion<QuantityClientParam> andUnits(String theSystem, String theUnits) {
return new StringCriterion(getParamName(), myToken1 + "|" + defaultString(theSystem) + "|" + defaultString(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; return myName;
} }
public ICriterion hasChainedProperty(ICriterion theICriterion) { public ICriterion<ReferenceClientParam> hasChainedProperty(ICriterion<?> theCriterion) {
return new ReferenceChainCriterion(getParamName(), theICriterion); return new ReferenceChainCriterion(getParamName(), theCriterion);
} }
/** /**
* Match the referenced resource if the resource has the given ID (this can be * Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource) * the logical ID or the absolute URL of the resource)
*/ */
public ICriterion hasId(IdDt theId) { public ICriterion<ReferenceClientParam> hasId(IdDt theId) {
return new StringCriterion(getParamName(), theId.getValue()); return new StringCriterion<ReferenceClientParam>(getParamName(), theId.getValue());
} }
/** /**
* Match the referenced resource if the resource has the given ID (this can be * Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource) * the logical ID or the absolute URL of the resource)
*/ */
public ICriterion hasId(String theId) { public ICriterion<ReferenceClientParam> hasId(String theId) {
return new StringCriterion(getParamName(), 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 String myParamName;
private ICriterionInternal myWrappedCriterion; private ICriterionInternal myWrappedCriterion;
public ReferenceChainCriterion(String theParamName, ICriterion theWrappedCriterion) { public ReferenceChainCriterion(String theParamName, ICriterion<?> theWrappedCriterion) {
myParamName = theParamName; myParamName = theParamName;
myWrappedCriterion = (ICriterionInternal) theWrappedCriterion; myWrappedCriterion = (ICriterionInternal) theWrappedCriterion;
} }

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.gclient; package ca.uhn.fhir.rest.gclient;
import static org.apache.commons.lang3.StringUtils.*;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
/* /*
@ -42,27 +43,27 @@ public class TokenClientParam implements IParam {
public IMatches exactly() { public IMatches exactly() {
return new IMatches() { return new IMatches() {
@Override @Override
public ICriterion systemAndCode(String theSystem, String theCode) { public ICriterion<TokenClientParam> systemAndCode(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), theSystem, theCode); return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
} }
@Override @Override
public ICriterion systemAndIdentifier(String theSystem, String theCode) { public ICriterion<TokenClientParam> systemAndIdentifier(String theSystem, String theCode) {
return new TokenCriterion(getParamName(), theSystem, theCode); return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
} }
@Override @Override
public ICriterion code(String theCode) { public ICriterion<TokenClientParam> code(String theCode) {
return new TokenCriterion(getParamName(), null, theCode); return new TokenCriterion(getParamName(), null, theCode);
} }
@Override @Override
public ICriterion identifier(String theIdentifier) { public ICriterion<TokenClientParam> identifier(String theIdentifier) {
return new TokenCriterion(getParamName(), null, theIdentifier); return new TokenCriterion(getParamName(), null, theIdentifier);
} }
@Override @Override
public ICriterion identifier(IdentifierDt theIdentifier) { public ICriterion<TokenClientParam> identifier(IdentifierDt theIdentifier) {
return new TokenCriterion(getParamName(), theIdentifier.getSystem().getValueAsString(), theIdentifier.getValue().getValue()); return new TokenCriterion(getParamName(), theIdentifier.getSystem().getValueAsString(), theIdentifier.getValue().getValue());
} }
}; };
@ -78,7 +79,7 @@ public class TokenClientParam implements IParam {
* The code * The code
* @return A criterion * @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 * Creates a search criterion that matches against the given system and identifier
@ -89,7 +90,7 @@ public class TokenClientParam implements IParam {
* The identifier * The identifier
* @return A criterion * @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 * 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 * The identifier
* @return A criterion * @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 * 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 * The identifier
* @return A criterion * @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) * 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 * The identifier
* @return A criterion * @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; 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 myValue;
private String myName; private String myName;
public TokenCriterion(String theName, String theSystem, String theCode) { public TokenCriterion(String theName, String theSystem, String theCode) {
myName = theName; myName = theName;
if (StringUtils.isNotBlank(theSystem)) { String system = ParameterUtil.escape(theSystem);
myValue = theSystem + "|" + StringUtils.defaultString(theCode); String code = ParameterUtil.escape(theCode);
if (StringUtils.isNotBlank(system)) {
myValue = system + "|" + StringUtils.defaultString(code);
} else if (system == null) {
myValue = StringUtils.defaultString(code);
} else { } 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()); throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
} }
myResourceType = theReturnResourceType;
if (theReturnResourceType != null) { if (theReturnResourceType != null) {
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class); if (IResource.class.isAssignableFrom(theReturnResourceType)) {
if (resourceDefAnnotation == null) { myResourceType = theReturnResourceType;
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation"); 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()); Integer count = RestfulServer.extractCountParameter(theRequest.getServletRequest());
boolean respondGzip=theRequest.isRespondGzip(); boolean respondGzip = theRequest.isRespondGzip();
IBundleProvider result = invokeServer(theRequest, params); IBundleProvider result = invokeServer(theRequest, params);
switch (getReturnType()) { switch (getReturnType()) {
@ -210,7 +212,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
} }
} }
/** /**
* Subclasses may override * Subclasses may override
* *

View File

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

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.rest.method; 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.IOException;
import java.io.PushbackReader; 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.Description;
import ca.uhn.fhir.model.api.annotation.TagListParam; import ca.uhn.fhir.model.api.annotation.TagListParam;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome; 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.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.Count; 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); 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) { public static void parseClientRequestResourceHeaders(Map<String, List<String>> theHeaders, IResource resource) {
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE); List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) { if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
@ -298,6 +330,7 @@ public class MethodUtil {
parameter.setName(((RequiredParam) nextAnnotation).name()); parameter.setName(((RequiredParam) nextAnnotation).name());
parameter.setRequired(true); parameter.setRequired(true);
parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes()); parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType); parameter.setType(parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations); MethodUtil.extractDescription(parameter, annotations);
param = parameter; param = parameter;
@ -306,6 +339,7 @@ public class MethodUtil {
parameter.setName(((OptionalParam) nextAnnotation).name()); parameter.setName(((OptionalParam) nextAnnotation).name());
parameter.setRequired(false); parameter.setRequired(false);
parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes()); parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType); parameter.setType(parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations); MethodUtil.extractDescription(parameter, annotations);
param = parameter; param = parameter;

View File

@ -25,14 +25,14 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr; 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.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterAndBinder implements IParamBinder { final class QueryParameterAndBinder extends BaseBinder<IQueryParameterAnd<?>> implements IParamBinder {
private final Class<? extends IQueryParameterAnd<?>> myType;
QueryParameterAndBinder(Class<? extends IQueryParameterAnd<?>> theType) { QueryParameterAndBinder(Class<? extends IQueryParameterAnd<?>> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
myType = theType; super(theType, theCompositeTypes);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -46,12 +46,8 @@ final class QueryParameterAndBinder implements IParamBinder {
public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterAnd<?> dt; IQueryParameterAnd<?> dt;
try { try {
dt = myType.newInstance(); dt = newInstance();
dt.setValuesAsQueryTokens(theString); dt.setValuesAsQueryTokens(theString);
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) { } catch (SecurityException e) {
throw new InternalErrorException(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.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr; 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.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterOrBinder implements IParamBinder { final class QueryParameterOrBinder extends BaseBinder<IQueryParameterOr<?>> implements IParamBinder {
private final Class<? extends IQueryParameterOr<?>> myType;
QueryParameterOrBinder(Class<? extends IQueryParameterOr<?>> theType) { QueryParameterOrBinder(Class<? extends IQueryParameterOr<?>> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
myType = theType; super(theType, theCompositeTypes);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -47,7 +47,7 @@ final class QueryParameterOrBinder implements IParamBinder {
public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterOr<?> dt; IQueryParameterOr<?> dt;
try { try {
dt = myType.newInstance(); dt = newInstance();
if (theString.size() == 0 || theString.get(0).size() == 0) { if (theString.size() == 0 || theString.get(0).size() == 0) {
return dt; return dt;
} }
@ -56,10 +56,6 @@ final class QueryParameterOrBinder implements IParamBinder {
} }
dt.setValuesAsQueryTokens(theString.get(0)); dt.setValuesAsQueryTokens(theString.get(0));
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) { } catch (SecurityException e) {
throw new InternalErrorException(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.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class QueryParameterTypeBinder implements IParamBinder { final class QueryParameterTypeBinder extends BaseBinder<IQueryParameterType> implements IParamBinder {
private final Class<? extends IQueryParameterType> myType;
QueryParameterTypeBinder(Class<? extends IQueryParameterType> theType) { QueryParameterTypeBinder(Class<? extends IQueryParameterType> theType, Class<? extends IQueryParameterType>[] theCompositeTypes) {
myType = theType; super(theType, theCompositeTypes);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -53,24 +52,16 @@ final class QueryParameterTypeBinder implements IParamBinder {
return null; return null;
} }
IQueryParameterType dt; IQueryParameterType dt = super.newInstance();
try {
dt = myType.newInstance(); if (theParams.size() == 0 || theParams.get(0).size() == 0) {
if (theParams.size() == 0 || theParams.get(0).size() == 0) { return dt;
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);
} }
if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected");
}
dt.setValueAsQueryToken(theParams.get(0).getQualifier(), value);
return dt; 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.model.primitive.StringDt;
import ca.uhn.fhir.rest.param.BaseQueryParameter; import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.param.CodingListParam; 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.DateAndListParam;
import ca.uhn.fhir.rest.param.DateOrListParam; import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
@ -70,12 +73,46 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class SearchParameter extends BaseQueryParameter { public class SearchParameter extends BaseQueryParameter {
private static HashMap<Class<?>, SearchParamTypeEnum> ourParamTypes; 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 Class<? extends IResource>[] myDeclaredTypes;
private String myDescription; private String myDescription;
private String myName; private String myName;
private IParamBinder myParamBinder; private IParamBinder myParamBinder;
private SearchParamTypeEnum myParamType; private SearchParamTypeEnum myParamType;
private boolean myRequired; private boolean myRequired;
private Class<?> myType; private Class<?> myType;
public SearchParameter() { public SearchParameter() {
@ -150,8 +187,12 @@ public class SearchParameter extends BaseQueryParameter {
return myParamBinder.parse(theString); return myParamBinder.parse(theString);
} }
public void setCompositeTypes(Class<? extends IQueryParameterType>[] theCompositeTypes) {
myCompositeTypes = theCompositeTypes;
}
public void setDeclaredTypes(Class<? extends IResource>[] theTypes) { public void setDeclaredTypes(Class<? extends IResource>[] theTypes) {
myDeclaredTypes=theTypes; myDeclaredTypes = theTypes;
} }
public void setDescription(String theDescription) { public void setDescription(String theDescription) {
@ -166,57 +207,27 @@ public class SearchParameter extends BaseQueryParameter {
this.myRequired = required; 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" }) @SuppressWarnings({ "unchecked" })
public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) { public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
this.myType = type; this.myType = type;
if (IQueryParameterType.class.isAssignableFrom(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)) { } 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)) { } 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)) { } else if (String.class.equals(type)) {
myParamBinder = new StringBinder(); myParamBinder = new StringBinder();
myParamType=SearchParamTypeEnum.STRING; myParamType = SearchParamTypeEnum.STRING;
} else { } else {
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName()); throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
} }
if (myParamType==null) { if (myParamType == null) {
myParamType=ourParamTypes.get(type); myParamType = ourParamTypes.get(type);
} }
if (myParamType!=null) { if (myParamType != null) {
// ok // ok
} else if (StringDt.class.isAssignableFrom(type)) { } else if (StringDt.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.STRING; myParamType = SearchParamTypeEnum.STRING;

View File

@ -26,8 +26,6 @@ import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource; 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.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam { class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
private Integer myIdParameterIndex; private Integer myIdParameterIndex;
private Integer myVersionIdParameterIndex; private Integer myVersionIdParameterIndex;
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) { 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"); throw new NullPointerException("ID can not be null");
} }
IdDt versionIdDt = null;
if (myVersionIdParameterIndex != 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(); 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++) { for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx); IParameter nextParam = getParameters().get(idx);
@ -128,40 +127,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return retVal; 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 @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.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.method.QualifiedParamList; import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -162,4 +163,12 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return b.toString(); 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) { // public static Integer findSinceParameterIndex(Method theMethod) {
// return findParamIndex(theMethod, Since.class); // return findParamIndex(theMethod, Since.class);
// } // }
public static int nonEscapedIndexOf(String theString, char theCharacter) { public static int nonEscapedIndexOf(String theString, char theCharacter) {
for (int i =0; i < theString.length(); i++) { for (int i = 0; i < theString.length(); i++) {
if (theString.charAt(i)==theCharacter) { if (theString.charAt(i) == theCharacter) {
if (i == 0 || theString.charAt(i-1) != '\\') { if (i == 0 || theString.charAt(i - 1) != '\\') {
return i; return i;
} }
} }
} }
return -1; return -1;
} }
public static Object fromInstant(Class<?> theType, InstantDt theArgument) { public static Object fromInstant(Class<?> theType, InstantDt theArgument) {
if (theType.equals(InstantDt.class)) { if (theType.equals(InstantDt.class)) {
if (theArgument == null) { if (theArgument == null) {
@ -138,19 +136,28 @@ public class ParameterUtil {
return null; 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>(); ArrayList<String> retVal = new ArrayList<String>();
if (theInput!=null) { if (theInput != null) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
for (int i = 0; i < theInput.length(); i++) { for (int i = 0; i < theInput.length(); i++) {
char next = theInput.charAt(i); char next = theInput.charAt(i);
if (next == ',') { if (next == theDelimiter) {
if (i == 0 || theInput.charAt(i-1) != '\\') { if (i == 0) {
b.append(next); b.append(next);
} else { } else {
if (b.length() > 0) { char prevChar = theInput.charAt(i - 1);
retVal.add(b.toString()); if (prevChar == '\\') {
b.setLength(0); b.append(next);
} else {
if (b.length() > 0) {
retVal.add(b.toString());
b.setLength(0);
}
} }
} }
} else { } else {
@ -161,28 +168,27 @@ public class ParameterUtil {
retVal.add(b.toString()); retVal.add(b.toString());
} }
} }
if (theUnescapeComponents) { if (theUnescapeComponents) {
for (int i = 0; i < retVal.size();i++) { for (int i = 0; i < retVal.size(); i++) {
retVal.set(i,unescape(retVal.get(i))); retVal.set(i, unescape(retVal.get(i)));
} }
} }
return retVal; return retVal;
} }
/** /**
* Escapes a string according to the rules for parameter escaping specified * Escapes a string according to the rules for parameter escaping specified in the <a
* in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a> * href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
*/ */
public static String escape(String theValue) { public static String escape(String theValue) {
if (theValue == null) { if (theValue == null) {
return theValue; return theValue;
} }
StringBuilder b = new StringBuilder(); 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); char next = theValue.charAt(i);
switch (next) { switch (next) {
case '$': case '$':
@ -194,31 +200,31 @@ public class ParameterUtil {
b.append(next); b.append(next);
} }
} }
return b.toString(); return b.toString();
} }
/** /**
* Unescapes a string according to the rules for parameter escaping specified * Unescapes a string according to the rules for parameter escaping specified in the <a
* in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a> * href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping Section</a>
*/ */
public static String unescape(String theValue) { public static String unescape(String theValue) {
if (theValue == null) { if (theValue == null) {
return theValue; return theValue;
} }
if (theValue.indexOf('\\')==-1) { if (theValue.indexOf('\\') == -1) {
return theValue; return theValue;
} }
StringBuilder b = new StringBuilder(); 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); char next = theValue.charAt(i);
if (next == '\\') { if (next == '\\') {
if (i == theValue.length()-1) { if (i == theValue.length() - 1) {
b.append(next); b.append(next);
} else { } else {
switch (theValue.charAt(i+1)) { switch (theValue.charAt(i + 1)) {
case '$': case '$':
case ',': case ',':
case '|': case '|':
@ -231,7 +237,7 @@ public class ParameterUtil {
b.append(next); b.append(next);
} }
} }
return b.toString(); return b.toString();
} }

View File

@ -27,6 +27,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
public class StringParam implements IQueryParameterType { public class StringParam implements IQueryParameterType {
@ -61,7 +62,7 @@ public class StringParam implements IQueryParameterType {
@Override @Override
public String getValueAsQueryToken() { public String getValueAsQueryToken() {
return myValue; return ParameterUtil.escape(myValue);
} }
public String getValueNotNull() { public String getValueNotNull() {
@ -91,7 +92,7 @@ public class StringParam implements IQueryParameterType {
} else { } else {
setExact(false); setExact(false);
} }
myValue = theValue; myValue = ParameterUtil.unescape(theValue);
} }
@Override @Override
@ -104,4 +105,8 @@ public class StringParam implements IQueryParameterType {
return builder.toString(); 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 java.util.List;
import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
/* /*
* #%L * #%L
@ -44,8 +45,20 @@ public class TokenOrListParam extends BaseOrListParam<TokenParam> {
return retVal; 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) { public void add(CodingDt theCodingDt) {
add(new TokenParam(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.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.composite.CodingDt; 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; import ca.uhn.fhir.rest.server.Constants;
public class TokenParam implements IQueryParameterType { public class TokenParam implements IQueryParameterType {
@ -61,7 +63,21 @@ public class TokenParam implements IQueryParameterType {
* @param theCodingDt The coding * @param theCodingDt The coding
*/ */
public TokenParam(CodingDt theCodingDt) { 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 @Override

View File

@ -418,7 +418,7 @@ public class RestfulServer extends HttpServlet {
IdDt versionId = null; IdDt versionId = null;
String operation = 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) == '/') { if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1); 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 * 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. * 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeSet;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
@ -153,7 +155,21 @@ public class FhirTerser {
} }
BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass()); BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
if (childElementDef == null) { 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); 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.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance; 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.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
@ -55,6 +56,25 @@ client.create()
//END SNIPPET: 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 //START SNIPPET: conformance
// Retrieve the server's conformance statement and print its description // Retrieve the server's conformance statement and print its description
Conformance conf = client.conformance(); Conformance conf = client.conformance();
@ -97,6 +117,16 @@ response = client.search()
.execute(); .execute();
//END SNIPPET: searchAdv //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 //START SNIPPET: searchPaging
if (response.getLinkNext().isEmpty() == false) { 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.ITestClient;
import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient; 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.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.QuantityParam;
@ -285,6 +286,24 @@ public List<Patient> searchByNamedQuery(@RequiredParam(name="someparam") StringP
} }
//END SNIPPET: searchNamedQuery //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 //START SNIPPET: searchIdentifierParam
@Search() @Search()
public List<Patient> searchByIdentifier(@RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theId) { public List<Patient> searchByIdentifier(@RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theId) {

View File

@ -89,6 +89,7 @@
</subsection> </subsection>
<subsection name="Type - Search/Query"> <subsection name="Type - Search/Query">
<p> <p>
The following example shows how to query using the generic client: The following example shows how to query using the generic client:
</p> </p>
@ -97,6 +98,8 @@
<param name="file" <param name="file"
value="src/site/example/java/example/GenericClientExample.java" /> value="src/site/example/java/example/GenericClientExample.java" />
</macro> </macro>
<h4>Search - Paging</h4>
<p> <p>
If the server supports paging results, the client has a page method If the server supports paging results, the client has a page method
which can be used to load subsequent pages. which can be used to load subsequent pages.
@ -106,6 +109,21 @@
<param name="file" <param name="file"
value="src/site/example/java/example/GenericClientExample.java" /> value="src/site/example/java/example/GenericClientExample.java" />
</macro> </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> <p>
The fluent search also has methods for sorting, limiting, specifying The fluent search also has methods for sorting, limiting, specifying
JSON encoding, etc. JSON encoding, etc.
@ -115,6 +133,7 @@
<param name="file" <param name="file"
value="src/site/example/java/example/GenericClientExample.java" /> value="src/site/example/java/example/GenericClientExample.java" />
</macro> </macro>
</subsection> </subsection>
<subsection name="Instance - Delete"> <subsection name="Instance - Delete">
@ -129,6 +148,23 @@
</macro> </macro>
</subsection> </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"> <subsection name="Server - Conformance">
<p> <p>
To retrieve the server's conformance statement, simply call the <code>conformance()</code> To retrieve the server's conformance statement, simply call the <code>conformance()</code>
@ -157,7 +193,7 @@
<section name="The Annotation-Driven Client"> <section name="The Annotation-Driven Client">
<p> <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>
<p> <p>

View File

@ -727,6 +727,34 @@
</subsection> </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"> <subsection name="Combining Multiple Parameters">
<p> <p>

View File

@ -10,7 +10,6 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; 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.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; 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.Bundle;
import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt; 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.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
@ -113,8 +112,11 @@ public class JsonParserTest {
" }" + " }" +
"}"; "}";
//@formatter:on //@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.InputStream;
import java.io.StringReader; import java.io.StringReader;
import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; 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.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.resource.Conformance; 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.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; 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.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; 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.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.QuantityParam;
@ -771,7 +774,7 @@ public class ClientTest {
} }
@Test @Test
public void testSearchComposite() throws Exception { public void testSearchOrList() throws Exception {
String msg = getPatientFeedWithOneResult(); String msg = getPatientFeedWithOneResult();
@ -944,6 +947,28 @@ public class ClientTest {
assertEquals("PRP1660", resource.getIdentifier().get(0).getValue().getValue()); 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 @Test
public void testSearchWithStringIncludes() throws Exception { public void testSearchWithStringIncludes() throws Exception {

View File

@ -4,6 +4,7 @@ import static org.junit.Assert.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import java.io.StringReader; import java.io.StringReader;
import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
@ -15,6 +16,7 @@ import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost; 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.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine; 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.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class GenericClientTest { 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 @Test
public void testDelete() throws Exception { public void testDelete() throws Exception {
OperationOutcome oo = new OperationOutcome(); 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()); 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 @Test

View File

@ -6,6 +6,7 @@ import java.util.List;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; 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.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; 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.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IBasicClient; 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.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.QuantityParam;
@ -40,7 +42,10 @@ public interface ITestClient extends IBasicClient {
@Search() @Search()
public List<Patient> getPatientByDateRange(@RequiredParam(name = "dateRange") DateRangeParam theIdentifiers); 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() @Search()
public List<Patient> getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate); 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 static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; 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.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server; 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.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -42,89 +41,103 @@ import ca.uhn.fhir.testutil.RandomServerPortProvider;
public class ResfulServerSelfReferenceTest { public class ResfulServerSelfReferenceTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResfulServerSelfReferenceTest.class); 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 CloseableHttpClient ourClient;
private static FhirContext ourCtx; private static FhirContext ourCtx;
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception { public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
ourCtx = new FhirContext(Patient.class); 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); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create(); HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager); builder.setConnectionManager(connectionManager);
ourClient = builder.build(); ourClient = builder.build();
} }
@AfterClass @Test
public static void afterClass() throws Exception { public void testContextWithSpace() throws Exception {
ourServer.stop(); 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 @Test
public void testSearchByParamIdentifier() throws Exception { public void testSearchByParamIdentifier() throws Exception {
int port = RandomServerPortProvider.findFreePort();
Server hServer = new Server(port);
String baseUri = "http://localhost:" + ourPort + "/fhir/context"; DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001"; ServerProfileProvider profProvider = new ServerProfileProvider(ourCtx);
HttpGet httpGet = new HttpGet(uri);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); ServletHandler proxyHandler = new ServletHandler();
ourLog.info("Response was:\n{}", responseContent); 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()); String responseContent = IOUtils.toString(status.getEntity().getContent());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); 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(1, bundle.getEntries().size());
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
assertEquals(uri, bundle.getLinkSelf().getValue()); Patient patient = (Patient) bundle.getEntries().get(0).getResource();
assertEquals(baseUri, bundle.getLinkBase().getValue()); 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. * Created by dsotnikov on 2/25/2014.
*/ */
@ -161,8 +174,6 @@ public class ResfulServerSelfReferenceTest {
return idToPatient; return idToPatient;
} }
@Search() @Search()
public Patient getPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) { public Patient getPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
for (Patient next : getIdToPatient().values()) { for (Patient next : getIdToPatient().values()) {
@ -174,8 +185,7 @@ public class ResfulServerSelfReferenceTest {
} }
return null; return null;
} }
/** /**
* Retrieve the resource by its identifier * 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.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortSpec;
public class SearchParameterMap extends HashMap<String, List<List<? extends IQueryParameterType>>> { 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 Set<Include> myIncludes;
private SortSpec mySort;
public void add(String theName, IQueryParameterAnd<?> theAnd) { public void add(String theName, IQueryParameterAnd<?> theAnd) {
if (theAnd == null) { if (theAnd == null) {
return; return;
@ -59,6 +62,10 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
get(theName).add(list); get(theName).add(list);
} }
public void addInclude(Include theInclude) {
getIncludes().add(theInclude);
}
public Set<Include> getIncludes() { public Set<Include> getIncludes() {
if (myIncludes == null) { if (myIncludes == null) {
myIncludes = new HashSet<Include>(); myIncludes = new HashSet<Include>();
@ -66,12 +73,16 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
return myIncludes; return myIncludes;
} }
public SortSpec getSort() {
return mySort;
}
public void setIncludes(Set<Include> theIncludes) { public void setIncludes(Set<Include> theIncludes) {
myIncludes = theIncludes; myIncludes = theIncludes;
} }
public void addInclude(Include theInclude) { public void setSort(SortSpec theSort) {
getIncludes().add(theInclude); mySort = theSort;
} }
@Override @Override

View File

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

View File

@ -31,34 +31,34 @@ function generateHapiSearch(json, container) {
var nextParam = json.params[i]; var nextParam = json.params[i];
var paramLine = null; var paramLine = null;
if (nextParam.type == 'string') { if (nextParam.type == 'string') {
paramLine = '.where(new StringParam("' + nextParam.name + '")'; paramLine = '.where(new StringClientParam("' + nextParam.name + '")';
paramLine += nextParam.qualifier = ':exact' ? '.matchesExactly()' : '.matches()'; paramLine += nextParam.qualifier = ':exact' ? '.matchesExactly()' : '.matches()';
paramLine += '.value("' + nextParam.value + '"))'; paramLine += '.value("' + nextParam.value + '"))';
} else if (nextParam.type == 'token') { } else if (nextParam.type == 'token') {
var idx = nextParam.value.indexOf('|'); var idx = nextParam.value.indexOf('|');
if (idx == -1) { if (idx == -1) {
paramLine = '.where(new TokenParam("' + nextParam.name + '").exactly().code("' + nextParam.value + '"))'; paramLine = '.where(new TokenClientParam("' + nextParam.name + '").exactly().code("' + nextParam.value + '"))';
} else { } 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') { } 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') { } else if (nextParam.type == 'reference') {
if (nextParam.qualifier == '') { if (nextParam.qualifier == '') {
if (nextParam.name.indexOf('.') == -1) { 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') { } else if (nextParam.type == 'date') {
var dateQual = nextParam.value.indexOf('T') == -1 ? 'day' : 'second'; var dateQual = nextParam.value.indexOf('T') == -1 ? 'day' : 'second';
if (nextParam.value.substring(0,2) == '>=') { 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) == '>') { } 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) == '<=') { } 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) == '<') { } 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) { if (paramLine != null) {

View File

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

View File

@ -15,6 +15,7 @@ public class SearchParameter {
private List<String> myTargetTypes; private List<String> myTargetTypes;
private String myType; private String myType;
private List<String> myCompositeOf; private List<String> myCompositeOf;
private List<String> myCompositeTypes;
public SearchParameter() { public SearchParameter() {
@ -124,4 +125,15 @@ public class SearchParameter {
return myCompositeOf; 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 java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NodeList; 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.AnyChild;
import ca.uhn.fhir.tinder.model.BaseElement; import ca.uhn.fhir.tinder.model.BaseElement;
import ca.uhn.fhir.tinder.model.BaseRootType; import ca.uhn.fhir.tinder.model.BaseRootType;
@ -261,7 +260,8 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
composite.setDescription(nextCompositeParam.getDescription()); composite.setDescription(nextCompositeParam.getDescription());
composite.setPath(nextCompositeParam.getPath()); composite.setPath(nextCompositeParam.getPath());
composite.setType("composite"); 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") @Description(shortDefinition="The resource identity")
@OptionalParam(name="_id") @OptionalParam(name="_id")
StringParam theId, StringParam theId,
#foreach ( $param in $searchParamsWithoutComposite ) #{if}(true) #{end} #foreach ( $param in $searchParams ) #{if}(true) #{end}
@Description(shortDefinition="${param.description}") @Description(shortDefinition="${param.description}")
#if (${param.type} == 'string' ) #if (${param.type} == 'string' )
@ -43,16 +43,16 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
DateRangeParam the${param.nameCapitalized}, DateRangeParam the${param.nameCapitalized},
#elseif (${param.type} == 'quantity' ) #elseif (${param.type} == 'quantity' )
@OptionalParam(name="${param.name}") @OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized}, QuantityAndListParam the${param.nameCapitalized},
#elseif (${param.type} == 'number' ) #elseif (${param.type} == 'number' )
@OptionalParam(name="${param.name}") @OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized}, NumberAndListParam the${param.nameCapitalized},
#elseif (${param.type} == 'reference' ) #elseif (${param.type} == 'reference' )
@OptionalParam(name="${param.name}", targetTypes={ #{foreach}($nextType in ${param.targetTypes}) ${nextType}.class #{if}($foreach.hasNext), #{end} #{end} } ) @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' ) #elseif (${param.type} == 'composite' )
@OptionalParam(name="${param.name}") @OptionalParam(name="${param.name}", compositeTypes= { ${param.compositeTypes[0]}Param.class, ${param.compositeTypes[1]}Param.class })
ReferenceParam the${param.nameCapitalized}, CompositeAndListParam<${param.compositeTypes[0]}Param, ${param.compositeTypes[1]}Param> the${param.nameCapitalized},
#end #end
#end #end
@ -71,7 +71,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
try { try {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("_id", theId); paramMap.add("_id", theId);
#foreach ( $param in $searchParamsWithoutComposite ) #foreach ( $param in $searchParams )
paramMap.add("${param.name}", the${param.nameCapitalized}); paramMap.add("${param.name}", the${param.nameCapitalized});
#end #end

View File

@ -60,7 +60,11 @@ public class ${className} extends BaseResource implements IResource {
* Path: <b>${param.path}</b><br/> * Path: <b>${param.path}</b><br/>
* </p> * </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}); public static final ${param.typeCapitalized}ClientParam ${param.fluentConstantName} = new ${param.typeCapitalized}ClientParam(${param.constantName});
#end
#if( ${param.typeCapitalized} == 'Reference' ) #if( ${param.typeCapitalized} == 'Reference' )
#foreach ( $include in $param.paths ) #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"> <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> <dependency-type>uses</dependency-type>
</dependent-module> </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="context-root" value="restful-server-example"/>
<property name="java-output-path" value="/restful-server-example/target/classes"/> <property name="java-output-path" value="/restful-server-example/target/classes"/>
</wb-module> </wb-module>

View File

@ -3,7 +3,7 @@
<groupId>ca.uhn.hapi.example</groupId> <groupId>ca.uhn.hapi.example</groupId>
<artifactId>restful-server-example</artifactId> <artifactId>restful-server-example</artifactId>
<version>0.3</version> <version>0.5-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>Sample RESTful Server (HAPI-FHIR)</name> <name>Sample RESTful Server (HAPI-FHIR)</name>
@ -17,6 +17,15 @@
<version>0.5-SNAPSHOT</version> <version>0.5-SNAPSHOT</version>
</dependency> </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 <!-- 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. --> logging library. Logback is used here, but log4j would also be fine. -->
<dependency> <dependency>
@ -48,6 +57,9 @@
<build> <build>
<plugins> <plugins>
<!--
Tell Maven which Java source version you want to use
-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
@ -57,13 +69,33 @@
<target>1.6</target> <target>1.6</target>
</configuration> </configuration>
</plugin> </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> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId> <artifactId>maven-deploy-plugin</artifactId>
<configuration> <configuration>
<skip>true</skip> <skip>false</skip>
</configuration> </configuration>
</plugin> </plugin>