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