001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.param; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.model.primitive.IdDt; 024import ca.uhn.fhir.rest.api.Constants; 025import ca.uhn.fhir.util.CoverageIgnore; 026import org.apache.commons.lang3.builder.ToStringBuilder; 027import org.apache.commons.lang3.builder.ToStringStyle; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029import org.hl7.fhir.instance.model.api.IIdType; 030 031import java.math.BigDecimal; 032 033import static ca.uhn.fhir.model.primitive.IdDt.isValidLong; 034import static org.apache.commons.lang3.StringUtils.isBlank; 035import static org.apache.commons.lang3.StringUtils.isNotBlank; 036 037public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ { 038 039 private String myChain; 040 private String myResourceType; 041 private String myBaseUrl; 042 private String myValue; 043 private String myIdPart; 044 private Boolean myMdmExpand; 045 046 /** 047 * Constructor 048 */ 049 public ReferenceParam() { 050 super(); 051 } 052 053 /** 054 * Constructor 055 */ 056 public ReferenceParam(String theValue) { 057 setValueAsQueryToken(null, null, null, theValue); 058 } 059 060 /** 061 * Constructor 062 */ 063 public ReferenceParam(String theChain, String theValue) { 064 setValueAsQueryToken(null, null, null, theValue); 065 setChain(theChain); 066 } 067 068 /** 069 * Constructor 070 */ 071 public ReferenceParam(String theResourceType, String theChain, String theValue) { 072 String qualifier = ""; 073 if (isNotBlank(theResourceType)) { 074 qualifier = ":" + theResourceType; 075 } 076 if (isNotBlank(theChain)) { 077 qualifier = qualifier + "." + theChain; 078 } 079 080 setValueAsQueryToken(null, null, qualifier, theValue); 081 } 082 083 /** 084 * Constructor 085 * 086 * @since 5.0.0 087 */ 088 public ReferenceParam(IIdType theValue) { 089 if (theValue != null) { 090 setValueAsQueryToken(null, null, null, theValue.getValue()); 091 } 092 } 093 094 095 private String defaultGetQueryParameterQualifier() { 096 StringBuilder b = new StringBuilder(); 097 if (isNotBlank(myChain)) { 098 if (isNotBlank(getResourceType())) { 099 b.append(':'); 100 b.append(getResourceType()); 101 } 102 b.append('.'); 103 b.append(myChain); 104 } 105 if (b.length() != 0) { 106 return b.toString(); 107 } 108 return null; 109 } 110 @Override 111 String doGetQueryParameterQualifier() { 112 return this.myMdmExpand != null ? ":mdm" : defaultGetQueryParameterQualifier(); 113 } 114 115 @Override 116 String doGetValueAsQueryToken(FhirContext theContext) { 117 if (isBlank(getResourceType())) { 118 return myValue; // e.g. urn:asdjd or 123 or cid:wieiuru or #1 119 } else { 120 if (isBlank(getChain()) && isNotBlank(getResourceType())) { 121 return getResourceType() + "/" + getIdPart(); 122 } 123 return myValue; 124 } 125 } 126 127 @Override 128 void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) { 129 if (Constants.PARAMQUALIFIER_MDM.equals(theQualifier)) { 130 myMdmExpand = true; 131 theQualifier = ""; 132 } 133 134 String q = theQualifier; 135 if (isNotBlank(q)) { 136 if (q.startsWith(":")) { 137 int nextIdx = q.indexOf('.'); 138 if (nextIdx != -1) { 139 myChain = q.substring(nextIdx + 1); 140 myResourceType = q.substring(1, nextIdx); 141 } else { 142 myChain = null; 143 myResourceType = q.substring(1); 144 } 145 146 myValue = theValue; 147 myIdPart = theValue; 148 149 IdDt id = new IdDt(theValue); 150 if (!id.hasBaseUrl() && id.hasIdPart() && id.hasResourceType()) { 151 if (id.getResourceType().equals(myResourceType)) { 152 myIdPart = id.getIdPart(); 153 } 154 } 155 156 } else if (q.startsWith(".")) { 157 myChain = q.substring(1); 158 myResourceType = null; 159 myValue = theValue; 160 myIdPart = theValue; 161 } 162 } else { 163 myChain = null; 164 myValue = theValue; 165 IdDt id = new IdDt(theValue); 166 myResourceType = id.getResourceType(); 167 myIdPart = id.getIdPart(); 168 myBaseUrl = id.getBaseUrl(); 169 } 170 171 } 172 173 174 @CoverageIgnore 175 public String getBaseUrl() { 176 return myBaseUrl; 177 } 178 179 public boolean isMdmExpand() { 180 return myMdmExpand != null && myMdmExpand; 181 } 182 183 public ReferenceParam setMdmExpand(boolean theMdmExpand) { 184 myMdmExpand = theMdmExpand; 185 return this; 186 } 187 188 public String getChain() { 189 return myChain; 190 } 191 192 public ReferenceParam setChain(String theChain) { 193 myChain = theChain; 194 return this; 195 } 196 197 @CoverageIgnore 198 public String getIdPart() { 199 return myIdPart; 200 } 201 202 @CoverageIgnore 203 public BigDecimal getIdPartAsBigDecimal() { 204 return new IdDt(myValue).getIdPartAsBigDecimal(); 205 } 206 207 @CoverageIgnore 208 public Long getIdPartAsLong() { 209 return new IdDt(myValue).getIdPartAsLong(); 210 } 211 212 public String getResourceType() { 213 if (isNotBlank(myResourceType)) { 214 return myResourceType; 215 } 216 if (isBlank(myChain)) { 217 return new IdDt(myValue).getResourceType(); 218 } 219 return null; 220 } 221 222 public Class<? extends IBaseResource> getResourceType(FhirContext theCtx) { 223 if (isBlank(getResourceType())) { 224 return null; 225 } 226 return theCtx.getResourceDefinition(getResourceType()).getImplementingClass(); 227 } 228 229 public String getValue() { 230 return myValue; 231 } 232 233 /** 234 * Note that the parameter to this method <b>must</b> be a resource reference, e.g 235 * <code>123</code> or <code>Patient/123</code> or <code>http://example.com/fhir/Patient/123</code> 236 * or something like this. This is not appropriate for cases where a chain is being used and 237 * the value is for a different type of parameter (e.g. a token). In that case, use one of the 238 * setter constructors. 239 */ 240 public ReferenceParam setValue(String theValue) { 241 IdDt id = new IdDt(theValue); 242 String qualifier = null; 243 if (id.hasResourceType()) { 244 qualifier = ":" + id.getResourceType(); 245 } 246 setValueAsQueryToken(null, null, qualifier, id.getIdPart()); 247 return this; 248 } 249 250 public boolean hasResourceType() { 251 return isNotBlank(myResourceType); 252 } 253 254 @Override 255 protected boolean isSupportsChain() { 256 return true; 257 } 258 259 /** 260 * Returns a new param containing the same value as this param, but with the type copnverted 261 * to {@link DateParam}. This is useful if you are using reference parameters and want to handle 262 * chained parameters of different types in a single method. 263 * <p> 264 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 265 * in the HAPI FHIR documentation for an example of how to use this method. 266 * </p> 267 */ 268 public DateParam toDateParam(FhirContext theContext) { 269 DateParam retVal = new DateParam(); 270 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 271 return retVal; 272 } 273 274 /** 275 * Returns a new param containing the same value as this param, but with the type copnverted 276 * to {@link NumberParam}. This is useful if you are using reference parameters and want to handle 277 * chained parameters of different types in a single method. 278 * <p> 279 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 280 * in the HAPI FHIR documentation for an example of how to use this method. 281 * </p> 282 */ 283 public NumberParam toNumberParam(FhirContext theContext) { 284 NumberParam retVal = new NumberParam(); 285 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 286 return retVal; 287 } 288 289 /** 290 * Returns a new param containing the same value as this param, but with the type copnverted 291 * to {@link QuantityParam}. This is useful if you are using reference parameters and want to handle 292 * chained parameters of different types in a single method. 293 * <p> 294 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 295 * in the HAPI FHIR documentation for an example of how to use this method. 296 * </p> 297 */ 298 public QuantityParam toQuantityParam(FhirContext theContext) { 299 QuantityParam retVal = new QuantityParam(); 300 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 301 return retVal; 302 } 303 304 @Override 305 public String toString() { 306 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 307 if (isNotBlank(myChain)) { 308 b.append("chain", myChain); 309 } 310 b.append("value", getValue()); 311 return b.build(); 312 } 313 314 /** 315 * Returns a new param containing the same value as this param, but with the type copnverted 316 * to {@link StringParam}. This is useful if you are using reference parameters and want to handle 317 * chained parameters of different types in a single method. 318 * <p> 319 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 320 * in the HAPI FHIR documentation for an example of how to use this method. 321 * </p> 322 */ 323 public StringParam toStringParam(FhirContext theContext) { 324 StringParam retVal = new StringParam(); 325 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 326 return retVal; 327 } 328 329 /** 330 * Returns a new param containing the same value as this param, but with the type copnverted 331 * to {@link TokenParam}. This is useful if you are using reference parameters and want to handle 332 * chained parameters of different types in a single method. 333 * <p> 334 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 335 * in the HAPI FHIR documentation for an example of how to use this method. 336 * </p> 337 */ 338 public TokenParam toTokenParam(FhirContext theContext) { 339 TokenParam retVal = new TokenParam(); 340 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 341 return retVal; 342 } 343 344 public boolean isIdPartValidLong() { 345 return isValidLong(getIdPart()); 346 } 347}