Introduce the ability for search parameters to have qualifiers, and add StringParameter to allow exact string matches

This commit is contained in:
jamesagnew 2014-05-16 08:34:31 -04:00
parent 54ab77d51a
commit 610bb542ca
30 changed files with 252 additions and 155 deletions

View File

@ -14,6 +14,10 @@
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
support quantity search parameters on the server (e.g. Observation.value-quantity) support quantity search parameters on the server (e.g. Observation.value-quantity)
</action> </action>
<action type="add">
Introduce StringParameter type which can be used as a RESTful operation search parameter
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
</action>
</release> </release>
</body> </body>
</document> </document>

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.model.api;
import java.util.List; import java.util.List;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public interface IQueryParameterAnd { public interface IQueryParameterAnd {
@ -34,7 +35,7 @@ public interface IQueryParameterAnd {
* for information on the <b>token</b> format * for information on the <b>token</b> format
* </p> * </p>
*/ */
public void setValuesAsQueryTokens(List<List<String>> theParameters) throws InvalidRequestException; public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException;
/** /**
* *

View File

@ -23,23 +23,30 @@ package ca.uhn.fhir.model.api;
public interface IQueryParameterType { public interface IQueryParameterType {
/** /**
* Sets the value of this type using the <b>token</b> format. This * Sets the value of this type using the <b>token</b> format. This format is used in HTTP queries as a parameter
* format is used in HTTP queries as a parameter format. * format.
* *
* <p>See FHIR specification * <p>
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search SearchParameter Types</a> * See FHIR specification <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search
* for information on the <b>token</b> format * SearchParameter Types</a> for information on the <b>token</b> format
* </p> * </p>
*
* @param theQualifier
* The parameter name qualifier that accompanied this value. For example, if the complete query was
* <code>http://foo?name:exact=John</code>, qualifier would be ":exact"
* @param theValue
* The actual parameter value. For example, if the complete query was
* <code>http://foo?name:exact=John</code>, the value would be "John"
*/ */
public void setValueAsQueryToken(String theParameter); public void setValueAsQueryToken(String theQualifier, String theValue);
/** /**
* Returns the value of this type using the <b>token</b> format. This * Returns the value of this type using the <b>token</b> format. This format is used in HTTP queries as a parameter
* format is used in HTTP queries as a parameter format. * format.
* *
* <p>See FHIR specification * <p>
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search SearchParameter Types</a> * See FHIR specification <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search
* for information on the <b>token</b> format * SearchParameter Types</a> for information on the <b>token</b> format
* </p> * </p>
*/ */
public String getValueAsQueryToken(); public String getValueAsQueryToken();

View File

@ -417,13 +417,13 @@ public class CodingDt
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
int barIndex = theParameter.indexOf('|'); int barIndex = theValue.indexOf('|');
if (barIndex != -1) { if (barIndex != -1) {
setSystem(new UriDt(theParameter.substring(0, barIndex))); setSystem(new UriDt(theValue.substring(0, barIndex)));
setCode(theParameter.substring(barIndex + 1)); setCode(theValue.substring(barIndex + 1));
} else { } else {
setCode(theParameter); setCode(theValue);
} }
} }

View File

@ -423,13 +423,13 @@ public class IdentifierDt
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
int barIndex = theParameter.indexOf('|'); int barIndex = theValue.indexOf('|');
if (barIndex != -1) { if (barIndex != -1) {
setSystem(new UriDt(theParameter.substring(0, barIndex))); setSystem(new UriDt(theValue.substring(0, barIndex)));
setValue(theParameter.substring(barIndex + 1)); setValue(theValue.substring(barIndex + 1));
} else { } else {
setValue(theParameter); setValue(theValue);
} }
} }

View File

@ -373,17 +373,17 @@ public class QuantityDt extends BaseElement implements ICompositeDatatype, IQuer
} }
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
setComparator((BoundCodeDt<QuantityCompararatorEnum>) null); setComparator((BoundCodeDt<QuantityCompararatorEnum>) null);
setCode((CodeDt) null); setCode((CodeDt) null);
setSystem((UriDt) null); setSystem((UriDt) null);
setUnits((StringDt) null); setUnits((StringDt) null);
setValue((DecimalDt) null); setValue((DecimalDt) null);
if (theParameter == null) { if (theValue == null) {
return; return;
} }
String[] parts = theParameter.split("\\|"); String[] parts = theValue.split("\\|");
if (parts.length > 0 && StringUtils.isNotBlank(parts[0])) { if (parts.length > 0 && StringUtils.isNotBlank(parts[0])) {
if (parts[0].startsWith("<=")) { if (parts[0].startsWith("<=")) {
setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS); setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS);

View File

@ -109,8 +109,8 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
setValue(theParameter); setValue(theValue);
} }
/** /**

View File

@ -59,7 +59,7 @@ public class StringParam implements IParam {
private class StringExactly implements IStringMatch { private class StringExactly implements IStringMatch {
@Override @Override
public ICriterion value(String theValue) { public ICriterion value(String theValue) {
return new StringCriterion(getParamName() + Constants.PARAMNAME_SUFFIX_EXACT, theValue); return new StringCriterion(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue);
} }
} }

View File

@ -0,0 +1,59 @@
package ca.uhn.fhir.rest.method;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class QualifiedParamList extends ArrayList<String> {
private static final long serialVersionUID = 1L;
private String myQualifier;
public QualifiedParamList() {
super();
}
public QualifiedParamList(int theCapacity) {
super(theCapacity);
}
public String getQualifier() {
return myQualifier;
}
public void setQualifier(String theQualifier) {
myQualifier = theQualifier;
}
public static QualifiedParamList singleton(String theQualifier, String theNextParam) {
QualifiedParamList retVal = new QualifiedParamList(1);
retVal.setQualifier(theQualifier);
retVal.add(theNextParam);
return retVal;
}
public static QualifiedParamList splitQueryStringByCommasIgnoreEscape(String theQualifier, String theParams){
QualifiedParamList retVal = new QualifiedParamList();
retVal.setQualifier(theQualifier);
StringTokenizer tok = new StringTokenizer(theParams,",");
String prev=null;
while (tok.hasMoreElements()) {
String str = tok.nextToken();
if (prev!=null&&prev.endsWith("\\")) {
int idx = retVal.size()-1;
String existing = retVal.get(idx);
retVal.set(idx, existing.substring(0, existing.length()-1) + "," + str);
}else {
retVal.add(str);
}
prev=str;
}
return retVal;
}
}

View File

@ -21,7 +21,10 @@ package ca.uhn.fhir.rest.method;
*/ */
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -45,6 +48,7 @@ public class Request {
private HttpServletRequest myServletRequest; private HttpServletRequest myServletRequest;
private HttpServletResponse myServletResponse; private HttpServletResponse myServletResponse;
private IdDt myVersion; private IdDt myVersion;
private Map<String,List<String>> myUnqualifiedToQualifiedNames;
public String getCompleteUrl() { public String getCompleteUrl() {
return myCompleteUrl; return myCompleteUrl;
@ -117,6 +121,33 @@ public class Request {
public void setParameters(Map<String, String[]> theParams) { public void setParameters(Map<String, String[]> theParams) {
myParameters = theParams; myParameters = theParams;
for (String next : myParameters.keySet()) {
for (int i = 0; i < next.length();i++) {
char nextChar = next.charAt(i);
if(nextChar == ':' || nextChar == '.') {
if (myUnqualifiedToQualifiedNames==null) {
myUnqualifiedToQualifiedNames = new HashMap<String, List<String>>();
}
String unqualified = next.substring(0,i);
List<String> list = myUnqualifiedToQualifiedNames.get(unqualified);
if (list==null) {
list=new ArrayList<String>(4);
myUnqualifiedToQualifiedNames.put(unqualified, list);
}
list.add(next);
break;
}
}
}
if (myUnqualifiedToQualifiedNames==null) {
myUnqualifiedToQualifiedNames=Collections.emptyMap();
}
}
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
return myUnqualifiedToQualifiedNames;
} }
public void setRequestType(RequestType theRequestType) { public void setRequestType(RequestType theRequestType) {

View File

@ -136,16 +136,29 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return false; return false;
} }
// This is used to track all the parameters so we can reject queries that
// have additional params we don't understand
Set<String> methodParamsTemp = new HashSet<String>(); Set<String> methodParamsTemp = new HashSet<String>();
Set<String> unqualifiedNames = theRequest.getUnqualifiedToQualifiedNames().keySet();
Set<String> qualifiedParamNames = theRequest.getParameters().keySet();
for (int i = 0; i < this.getParameters().size(); i++) { for (int i = 0; i < this.getParameters().size(); i++) {
if (!(getParameters().get(i) instanceof BaseQueryParameter)) { if (!(getParameters().get(i) instanceof BaseQueryParameter)) {
continue; continue;
} }
BaseQueryParameter temp = (BaseQueryParameter) getParameters().get(i); BaseQueryParameter temp = (BaseQueryParameter) getParameters().get(i);
methodParamsTemp.add(temp.getName()); String name = temp.getName();
if (temp.isRequired() && !theRequest.getParameters().containsKey(temp.getName())) {eee if (temp.isRequired()) {
ourLog.trace("Method {} doesn't match param '{}' is not present", getMethod().getName(), temp.getName());
if (qualifiedParamNames.contains(name)) {
methodParamsTemp.add(name);
} else if (unqualifiedNames.contains(name)) {
methodParamsTemp.addAll(theRequest.getUnqualifiedToQualifiedNames().get(name));
} else {
ourLog.trace("Method {} doesn't match param '{}' is not present", getMethod().getName(), name);
return false; return false;
}
} }
} }
if (myQueryName != null) { if (myQueryName != null) {

View File

@ -23,16 +23,15 @@ package ca.uhn.fhir.rest.param;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.rest.client.BaseClientInvocation; import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.Request; import ca.uhn.fhir.rest.method.Request;
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;
import ca.uhn.fhir.util.QueryUtil;
public abstract class BaseQueryParameter implements IParameter { public abstract class BaseQueryParameter implements IParameter {
@ -40,13 +39,13 @@ public abstract class BaseQueryParameter implements IParameter {
public abstract String getName(); public abstract String getName();
public abstract Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException; public abstract Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException;
public abstract boolean isRequired(); public abstract boolean isRequired();
/** /**
* Parameter should return true if {@link #parse(List)} should be called even * Parameter should return true if {@link #parse(List)} should be called even if the query string contained no
* if the query string contained no values for the given parameter * values for the given parameter
*/ */
public abstract boolean handlesMissing(); public abstract boolean handlesMissing();
@ -79,28 +78,43 @@ public abstract class BaseQueryParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
String[] value = theRequest.getParameters().get(getName());
if (value == null || value.length == 0) { List<QualifiedParamList> paramList = new ArrayList<QualifiedParamList>();
String name = getName();
parseParams(theRequest, paramList, name, null);
List<String> qualified = theRequest.getUnqualifiedToQualifiedNames().get(name);
if (qualified != null) {
for (String nextQualified : qualified) {
parseParams(theRequest, paramList, nextQualified, nextQualified.substring(name.length()));
}
}
if (paramList.isEmpty()) {
if (handlesMissing()) { if (handlesMissing()) {
return parse(new ArrayList<List<String>>(0)); return paramList;
} else { } else {
return null; return null;
} }
} }
List<List<String>> paramList = new ArrayList<List<String>>(value.length);
for (String nextParam : value) {
if (nextParam.contains(",") == false) {
paramList.add(Collections.singletonList(nextParam));
} else {
paramList.add(QueryUtil.splitQueryStringByCommasIgnoreEscape(nextParam));
}
}
return parse(paramList); return parse(paramList);
} }
private void parseParams(Request theRequest, List<QualifiedParamList> paramList, String theQualifiedParamName, String theQualifier) {
String[] value = theRequest.getParameters().get(theQualifiedParamName);
if (value != null) {
for (String nextParam : value) {
if (nextParam.contains(",") == false) {
paramList.add(QualifiedParamList.singleton(theQualifier, nextParam));
} else {
paramList.add(QualifiedParamList.splitQueryStringByCommasIgnoreEscape(theQualifier, nextParam));
}
}
}
}
@Override @Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) { public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// ignore for now // ignore for now

View File

@ -116,7 +116,7 @@ public class CodingListParam implements IQueryParameterOr, Iterable<CodingDt> {
getCodings().clear(); getCodings().clear();
for (String string : theParameters) { for (String string : theParameters) {
CodingDt dt = new CodingDt(); CodingDt dt = new CodingDt();
dt.setValueAsQueryToken(string); dt.setValueAsQueryToken(null, string);
myCodings.add(dt); myCodings.add(dt);
} }
} }

View File

@ -28,6 +28,7 @@ import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParam implements IQueryParameterAnd { public class DateRangeParam implements IQueryParameterAnd {
@ -210,7 +211,7 @@ public class DateRangeParam implements IQueryParameterAnd {
} }
@Override @Override
public void setValuesAsQueryTokens(List<List<String>> theParameters) throws InvalidRequestException { public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException {
for (List<String> paramList : theParameters) { for (List<String> paramList : theParameters) {
if (paramList.size() == 0) { if (paramList.size() == 0) {
continue; continue;
@ -220,7 +221,7 @@ public class DateRangeParam implements IQueryParameterAnd {
} }
String param = paramList.get(0); String param = paramList.get(0);
QualifiedDateParam parsed = new QualifiedDateParam(); QualifiedDateParam parsed = new QualifiedDateParam();
parsed.setValueAsQueryToken(param); parsed.setValueAsQueryToken(null, param);
addParam(parsed); addParam(parsed);
} }
} }

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param;
import java.util.List; import java.util.List;
import ca.uhn.fhir.rest.method.QualifiedParamList;
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;
@ -29,6 +30,6 @@ interface IParamBinder {
List<List<String>> encode(Object theString) throws InternalErrorException; List<List<String>> encode(Object theString) throws InternalErrorException;
Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException; Object parse(List<QualifiedParamList> theList) throws InternalErrorException, InvalidRequestException;
} }

View File

@ -57,7 +57,7 @@ public class IdentifierListParam implements IQueryParameterOr {
public void setValuesAsQueryTokens(List<String> theParameters) { public void setValuesAsQueryTokens(List<String> theParameters) {
for (String string : theParameters) { for (String string : theParameters) {
IdentifierDt dt = new IdentifierDt(); IdentifierDt dt = new IdentifierDt();
dt.setValueAsQueryToken(string); dt.setValueAsQueryToken(null, string);
myIdentifiers.add(dt); myIdentifiers.add(dt);
} }
} }

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.method.QualifiedParamList;
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;
@ -83,7 +84,7 @@ public class IncludeParameter extends BaseQueryParameter {
} }
@Override @Override
public Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
Collection<PathSpecification> retValCollection = null; Collection<PathSpecification> retValCollection = null;
if (myInstantiableCollectionType!=null) { if (myInstantiableCollectionType!=null) {
try { try {

View File

@ -59,7 +59,7 @@ public class QualifiedDateParam extends DateTimeDt implements IQueryParameterTyp
* @param theString The string * @param theString The string
*/ */
public QualifiedDateParam(String theString) { public QualifiedDateParam(String theString) {
setValueAsQueryToken(theString); setValueAsQueryToken(null, theString);
} }
/** /**
@ -95,28 +95,28 @@ public class QualifiedDateParam extends DateTimeDt implements IQueryParameterTyp
} }
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
if (theParameter.length() < 2) { if (theValue.length() < 2) {
throw new DataFormatException("Invalid qualified date parameter: "+theParameter); throw new DataFormatException("Invalid qualified date parameter: "+theValue);
} }
char char0 = theParameter.charAt(0); char char0 = theValue.charAt(0);
char char1 = theParameter.charAt(1); char char1 = theValue.charAt(1);
if (Character.isDigit(char0)) { if (Character.isDigit(char0)) {
setValueAsString(theParameter); setValueAsString(theValue);
} else { } else {
int dateStart = 2; int dateStart = 2;
if (Character.isDigit(char1)) { if (Character.isDigit(char1)) {
dateStart = 1; dateStart = 1;
} }
String comparatorString = theParameter.substring(0, dateStart); String comparatorString = theValue.substring(0, dateStart);
QuantityCompararatorEnum comparator = QuantityCompararatorEnum.VALUESET_BINDER.fromCodeString(comparatorString); QuantityCompararatorEnum comparator = QuantityCompararatorEnum.VALUESET_BINDER.fromCodeString(comparatorString);
if (comparator==null) { if (comparator==null) {
throw new DataFormatException("Invalid date qualifier: "+comparatorString); throw new DataFormatException("Invalid date qualifier: "+comparatorString);
} }
String dateString = theParameter.substring(dateStart); String dateString = theValue.substring(dateStart);
setValueAsString(dateString); setValueAsString(dateString);
setComparator(comparator); setComparator(comparator);
} }

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.param;
import java.util.List; import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.rest.method.QualifiedParamList;
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;
@ -40,7 +41,7 @@ final class QueryParameterAndBinder implements IParamBinder {
} }
@Override @Override
public Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterAnd dt; IQueryParameterAnd dt;
try { try {
dt = myType.newInstance(); dt = myType.newInstance();

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.method.QualifiedParamList;
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;
@ -41,7 +42,7 @@ final class QueryParameterOrBinder implements IParamBinder {
} }
@Override @Override
public Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
IQueryParameterOr dt; IQueryParameterOr dt;
try { try {
dt = myType.newInstance(); dt = myType.newInstance();

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.method.QualifiedParamList;
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;
@ -41,18 +42,18 @@ final class QueryParameterTypeBinder implements IParamBinder {
} }
@Override @Override
public Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theParams) throws InternalErrorException, InvalidRequestException {
IQueryParameterType dt; IQueryParameterType dt;
try { try {
dt = myType.newInstance(); dt = myType.newInstance();
if (theString.size() == 0 || theString.get(0).size() == 0) { if (theParams.size() == 0 || theParams.get(0).size() == 0) {
return dt; return dt;
} }
if (theString.size() > 1 || theString.get(0).size() > 1) { if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected"); throw new InvalidRequestException("Multiple values detected");
} }
dt.setValueAsQueryToken(theString.get(0).get(0)); dt.setValueAsQueryToken(theParams.get(0).getQualifier(), theParams.get(0).get(0));
} catch (InstantiationException e) { } catch (InstantiationException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

@ -13,17 +13,17 @@ public class ReferenceParam implements IQueryParameterType {
} }
public ReferenceParam(String theValue) { public ReferenceParam(String theValue) {
setValueAsQueryToken(theValue); setValueAsQueryToken(null, theValue);
} }
public ReferenceParam(String theChain, String theValue) { public ReferenceParam(String theChain, String theValue) {
setValueAsQueryToken(theValue); setValueAsQueryToken(null, theValue);
setChain(theChain); setChain(theChain);
} }
public ReferenceParam(Class<? extends IResource> theType, String theChain, String theValue) { public ReferenceParam(Class<? extends IResource> theType, String theChain, String theValue) {
setType(theType); setType(theType);
setValueAsQueryToken(theValue); setValueAsQueryToken(null, theValue);
setChain(theChain); setChain(theChain);
} }
@ -49,8 +49,8 @@ public class ReferenceParam implements IQueryParameterType {
} }
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
myValue=theParameter; myValue=theValue;
} }
} }

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; 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.method.QualifiedParamList;
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;
@ -97,7 +98,7 @@ public class SearchParameter extends BaseQueryParameter {
* @see ca.uhn.fhir.rest.param.IParameter#parse(java.util.List) * @see ca.uhn.fhir.rest.param.IParameter#parse(java.util.List)
*/ */
@Override @Override
public Object parse(List<List<String>> theString) throws InternalErrorException, InvalidRequestException { public Object parse(List<QualifiedParamList> theString) throws InternalErrorException, InvalidRequestException {
return myParamBinder.parse(theString); return myParamBinder.parse(theString);
} }

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.param; package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.Constants;
public class StringParameter extends StringDt { public class StringParameter extends StringDt {
@ -13,6 +14,16 @@ public class StringParameter extends StringDt {
setValue(theValue); setValue(theValue);
} }
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_STRING_EXACT.equals(theQualifier)) {
setExact(true);
}else {
setExact(false);
}
super.setValueAsQueryToken(theQualifier, theValue);
}
public boolean isExact() { public boolean isExact() {
return myExact; return myExact;
} }

View File

@ -74,7 +74,7 @@ public class Constants {
public static final String FORMAT_XML = "xml"; public static final String FORMAT_XML = "xml";
public static final String FORMAT_JSON = "json"; public static final String FORMAT_JSON = "json";
public static final String PARAM_INCLUDE = "_include"; public static final String PARAM_INCLUDE = "_include";
public static final String PARAMNAME_SUFFIX_EXACT = ":exact"; public static final String PARAMQUALIFIER_STRING_EXACT = ":exact";
static { static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>(); Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -1,50 +0,0 @@
package ca.uhn.fhir.util;
/*
* #%L
* HAPI FHIR 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.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class QueryUtil {
public static List<String> splitQueryStringByCommasIgnoreEscape(String theInput){
ArrayList<String> retVal = new ArrayList<String>();
StringTokenizer tok = new StringTokenizer(theInput,",");
String prev=null;
while (tok.hasMoreElements()) {
String str = tok.nextToken();
if (prev!=null&&prev.endsWith("\\")) {
int idx = retVal.size()-1;
String existing = retVal.get(idx);
retVal.set(idx, existing.substring(0, existing.length()-1) + "," + str);
}else {
retVal.add(str);
}
prev=str;
}
return retVal;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.testutil; package ca.uhn.fhir.rest.method;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -6,20 +6,20 @@ import java.util.List;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.util.QueryUtil; import ca.uhn.fhir.rest.method.QualifiedParamList;
public class QueryUtilTest { public class QualifiedParamListTest {
@Test @Test
public void testSplit1() { public void testSplit1() {
List<String> actual = QueryUtil.splitQueryStringByCommasIgnoreEscape("aaa"); List<String> actual = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null,"aaa");
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals("aaa", actual.get(0)); assertEquals("aaa", actual.get(0));
} }
@Test @Test
public void testSplit2() { public void testSplit2() {
List<String> actual = QueryUtil.splitQueryStringByCommasIgnoreEscape("aaa,bbb"); List<String> actual = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null,"aaa,bbb");
assertEquals(2, actual.size()); assertEquals(2, actual.size());
assertEquals("aaa", actual.get(0)); assertEquals("aaa", actual.get(0));
assertEquals("bbb", actual.get(1)); assertEquals("bbb", actual.get(1));
@ -27,7 +27,7 @@ public class QueryUtilTest {
@Test @Test
public void testSplit3() { public void testSplit3() {
List<String> actual = QueryUtil.splitQueryStringByCommasIgnoreEscape("aaa,b\\,bb"); List<String> actual = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null,"aaa,b\\,bb");
System.out.println(actual); System.out.println(actual);
assertEquals(2, actual.size()); assertEquals(2, actual.size());
assertEquals("aaa", actual.get(0)); assertEquals("aaa", actual.get(0));

View File

@ -5,13 +5,13 @@ import static org.junit.Assert.*;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParamTest { public class DateRangeParamTest {
@ -90,9 +90,9 @@ public class DateRangeParamTest {
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException { private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam(); DateRangeParam p = new DateRangeParam();
List<List<String>> tokens=new ArrayList<List<String>>(); List<QualifiedParamList> tokens=new ArrayList<QualifiedParamList>();
tokens.add(Collections.singletonList(theLower)); tokens.add(QualifiedParamList.singleton(null,theLower));
tokens.add(Collections.singletonList(theUpper)); tokens.add(QualifiedParamList.singleton(null,theUpper));
p.setValuesAsQueryTokens(tokens); p.setValuesAsQueryTokens(tokens);
return p; return p;
} }

View File

@ -387,13 +387,13 @@ public class IdentifierDt
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void setValueAsQueryToken(String theParameter) { public void setValueAsQueryToken(String theQualifier, String theValue) {
int barIndex = theParameter.indexOf('|'); int barIndex = theValue.indexOf('|');
if (barIndex != -1) { if (barIndex != -1) {
setSystem(new UriDt(theParameter.substring(0, barIndex))); setSystem(new UriDt(theValue.substring(0, barIndex)));
setValue(theParameter.substring(barIndex + 1)); setValue(theValue.substring(barIndex + 1));
} else { } else {
setValue(theParameter); setValue(theValue);
} }
} }

View File

@ -996,19 +996,19 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return new QualifiedDateParam(theValueAsQueryToken); return new QualifiedDateParam(theValueAsQueryToken);
case NUMBER: case NUMBER:
QuantityDt qt = new QuantityDt(); QuantityDt qt = new QuantityDt();
qt.setValueAsQueryToken(theValueAsQueryToken); qt.setValueAsQueryToken(null, theValueAsQueryToken);
return qt; return qt;
case QUANTITY: case QUANTITY:
qt = new QuantityDt(); qt = new QuantityDt();
qt.setValueAsQueryToken(theValueAsQueryToken); qt.setValueAsQueryToken(null, theValueAsQueryToken);
return qt; return qt;
case STRING: case STRING:
StringDt st = new StringDt(); StringDt st = new StringDt();
st.setValueAsQueryToken(theValueAsQueryToken); st.setValueAsQueryToken(null, theValueAsQueryToken);
return st; return st;
case TOKEN: case TOKEN:
IdentifierDt id = new IdentifierDt(); IdentifierDt id = new IdentifierDt();
id.setValueAsQueryToken(theValueAsQueryToken); id.setValueAsQueryToken(null, theValueAsQueryToken);
return id; return id;
case COMPOSITE: case COMPOSITE:
case REFERENCE: case REFERENCE: