001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 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 private String defaultGetQueryParameterQualifier() { 095 StringBuilder b = new StringBuilder(); 096 if (isNotBlank(myChain)) { 097 if (isNotBlank(getResourceType())) { 098 b.append(':'); 099 b.append(getResourceType()); 100 } 101 b.append('.'); 102 b.append(myChain); 103 } 104 if (b.length() != 0) { 105 return b.toString(); 106 } 107 return null; 108 } 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 @CoverageIgnore 173 public String getBaseUrl() { 174 return myBaseUrl; 175 } 176 177 public boolean isMdmExpand() { 178 return myMdmExpand != null && myMdmExpand; 179 } 180 181 public ReferenceParam setMdmExpand(boolean theMdmExpand) { 182 myMdmExpand = theMdmExpand; 183 return this; 184 } 185 186 public String getChain() { 187 return myChain; 188 } 189 190 public ReferenceParam setChain(String theChain) { 191 myChain = theChain; 192 return this; 193 } 194 195 @CoverageIgnore 196 public String getIdPart() { 197 return myIdPart; 198 } 199 200 @CoverageIgnore 201 public BigDecimal getIdPartAsBigDecimal() { 202 return new IdDt(myValue).getIdPartAsBigDecimal(); 203 } 204 205 @CoverageIgnore 206 public Long getIdPartAsLong() { 207 return new IdDt(myValue).getIdPartAsLong(); 208 } 209 210 public String getResourceType() { 211 if (isNotBlank(myResourceType)) { 212 return myResourceType; 213 } 214 if (isBlank(myChain)) { 215 return new IdDt(myValue).getResourceType(); 216 } 217 return null; 218 } 219 220 public Class<? extends IBaseResource> getResourceType(FhirContext theCtx) { 221 if (isBlank(getResourceType())) { 222 return null; 223 } 224 return theCtx.getResourceDefinition(getResourceType()).getImplementingClass(); 225 } 226 227 public String getValue() { 228 return myValue; 229 } 230 231 /** 232 * Note that the parameter to this method <b>must</b> be a resource reference, e.g 233 * <code>123</code> or <code>Patient/123</code> or <code>http://example.com/fhir/Patient/123</code> 234 * or something like this. This is not appropriate for cases where a chain is being used and 235 * the value is for a different type of parameter (e.g. a token). In that case, use one of the 236 * setter constructors. 237 */ 238 public ReferenceParam setValue(String theValue) { 239 IdDt id = new IdDt(theValue); 240 String qualifier = null; 241 if (id.hasResourceType()) { 242 qualifier = ":" + id.getResourceType(); 243 } 244 setValueAsQueryToken(null, null, qualifier, id.getIdPart()); 245 return this; 246 } 247 248 public boolean hasResourceType() { 249 return isNotBlank(myResourceType); 250 } 251 252 @Override 253 protected boolean isSupportsChain() { 254 return true; 255 } 256 257 /** 258 * Returns a new param containing the same value as this param, but with the type copnverted 259 * to {@link DateParam}. This is useful if you are using reference parameters and want to handle 260 * chained parameters of different types in a single method. 261 * <p> 262 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 263 * in the HAPI FHIR documentation for an example of how to use this method. 264 * </p> 265 */ 266 public DateParam toDateParam(FhirContext theContext) { 267 DateParam retVal = new DateParam(); 268 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 269 return retVal; 270 } 271 272 /** 273 * Returns a new param containing the same value as this param, but with the type copnverted 274 * to {@link NumberParam}. This is useful if you are using reference parameters and want to handle 275 * chained parameters of different types in a single method. 276 * <p> 277 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 278 * in the HAPI FHIR documentation for an example of how to use this method. 279 * </p> 280 */ 281 public NumberParam toNumberParam(FhirContext theContext) { 282 NumberParam retVal = new NumberParam(); 283 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 284 return retVal; 285 } 286 287 /** 288 * Returns a new param containing the same value as this param, but with the type copnverted 289 * to {@link QuantityParam}. This is useful if you are using reference parameters and want to handle 290 * chained parameters of different types in a single method. 291 * <p> 292 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 293 * in the HAPI FHIR documentation for an example of how to use this method. 294 * </p> 295 */ 296 public QuantityParam toQuantityParam(FhirContext theContext) { 297 QuantityParam retVal = new QuantityParam(); 298 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 299 return retVal; 300 } 301 302 @Override 303 public String toString() { 304 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 305 if (isNotBlank(myChain)) { 306 b.append("chain", myChain); 307 } 308 b.append("value", getValue()); 309 return b.build(); 310 } 311 312 /** 313 * Returns a new param containing the same value as this param, but with the type copnverted 314 * to {@link StringParam}. This is useful if you are using reference parameters and want to handle 315 * chained parameters of different types in a single method. 316 * <p> 317 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 318 * in the HAPI FHIR documentation for an example of how to use this method. 319 * </p> 320 */ 321 public StringParam toStringParam(FhirContext theContext) { 322 StringParam retVal = new StringParam(); 323 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 324 return retVal; 325 } 326 327 /** 328 * Returns a new param containing the same value as this param, but with the type copnverted 329 * to {@link TokenParam}. This is useful if you are using reference parameters and want to handle 330 * chained parameters of different types in a single method. 331 * <p> 332 * See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_search.html#chained-resource-references">Dynamic Chains</a> 333 * in the HAPI FHIR documentation for an example of how to use this method. 334 * </p> 335 */ 336 public TokenParam toTokenParam(FhirContext theContext) { 337 TokenParam retVal = new TokenParam(); 338 retVal.setValueAsQueryToken(theContext, null, null, getValueAsQueryToken(theContext)); 339 return retVal; 340 } 341 342 public boolean isIdPartValidLong() { 343 return isValidLong(getIdPart()); 344 } 345}