
001package ca.uhn.fhir.model.api; 002 003import ca.uhn.fhir.i18n.Msg; 004import org.apache.commons.lang3.builder.ToStringBuilder; 005 006import java.io.Serializable; 007 008import static org.apache.commons.lang3.StringUtils.defaultString; 009import static org.apache.commons.lang3.StringUtils.isBlank; 010import static org.apache.commons.lang3.StringUtils.isNotBlank; 011 012/* 013 * #%L 014 * HAPI FHIR - Core Library 015 * %% 016 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 017 * %% 018 * Licensed under the Apache License, Version 2.0 (the "License"); 019 * you may not use this file except in compliance with the License. 020 * You may obtain a copy of the License at 021 * 022 * http://www.apache.org/licenses/LICENSE-2.0 023 * 024 * Unless required by applicable law or agreed to in writing, software 025 * distributed under the License is distributed on an "AS IS" BASIS, 026 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 027 * See the License for the specific language governing permissions and 028 * limitations under the License. 029 * #L% 030 */ 031 032/** 033 * Represents a FHIR resource path specification, e.g. <code>Patient:name</code> 034 * <p> 035 * Note on equality: This class uses {@link #getValue() value} and the {@link #isRecurse() recurse} properties to test 036 * equality. Prior to HAPI 1.2 (and FHIR DSTU2) the recurse property did not exist, so this may merit consideration when 037 * upgrading servers. 038 * </p> 039 * <p> 040 * Note on thread safety: This class is not thread safe. 041 * </p> 042 */ 043public class Include implements Serializable { 044 045 private static final long serialVersionUID = 1L; 046 047 private final boolean myImmutable; 048 private boolean myIterate; 049 private String myValue; 050 private String myParamType; 051 private String myParamName; 052 private String myParamTargetType; 053 054 /** 055 * Constructor for <b>non-recursive</b> include 056 * 057 * @param theValue 058 * The <code>_include</code> value, e.g. "Patient:name" 059 */ 060 public Include(String theValue) { 061 this(theValue, false); 062 } 063 064 /** 065 * Constructor for an include 066 * 067 * @param theValue 068 * The <code>_include</code> value, e.g. "Patient:name" 069 * @param theIterate 070 * Should the include recurse 071 */ 072 public Include(String theValue, boolean theIterate) { 073 this(theValue, theIterate, false); 074 } 075 076 /** 077 * Constructor for an include 078 * 079 * @param theValue 080 * The <code>_include</code> value, e.g. "Patient:name" 081 * @param theIterate 082 * Should the include recurse 083 */ 084 public Include(String theValue, boolean theIterate, boolean theImmutable) { 085 setValue(theValue); 086 myIterate = theIterate; 087 myImmutable = theImmutable; 088 } 089 090 /** 091 * Creates a copy of this include with non-recurse behaviour 092 */ 093 public Include asNonRecursive() { 094 return new Include(myValue, false); 095 } 096 097 /** 098 * Creates a copy of this include with recurse behaviour 099 */ 100 public Include asRecursive() { 101 return new Include(myValue, true); 102 } 103 104 /** 105 * See the note on equality on the {@link Include class documentation} 106 */ 107 @Override 108 public boolean equals(Object obj) { 109 if (this == obj) { 110 return true; 111 } 112 if (obj == null) { 113 return false; 114 } 115 if (getClass() != obj.getClass()) { 116 return false; 117 } 118 Include other = (Include) obj; 119 if (myIterate != other.myIterate) { 120 return false; 121 } 122 if (myValue == null) { 123 if (other.myValue != null) { 124 return false; 125 } 126 } else if (!myValue.equals(other.myValue)) { 127 return false; 128 } 129 return true; 130 } 131 132 /** 133 * Returns the portion of the value before the first colon 134 */ 135 public String getParamType() { 136 return myParamType; 137 } 138 139 /** 140 * Returns the portion of the value after the first colon but before the second colon 141 */ 142 public String getParamName() { 143 return myParamName; 144 } 145 146 /** 147 * Returns the portion of the string after the second colon, or null if there are not two colons in the value. 148 */ 149 public String getParamTargetType() { 150 return myParamTargetType; 151 152 } 153 154 public String getValue() { 155 return myValue; 156 } 157 158 /** 159 * See the note on equality on the {@link Include class documentation} 160 */ 161 @Override 162 public int hashCode() { 163 final int prime = 31; 164 int result = 1; 165 result = prime * result + (myIterate ? 1231 : 1237); 166 result = prime * result + ((myValue == null) ? 0 : myValue.hashCode()); 167 return result; 168 } 169 170 /** 171 * Is this object {@link #toLocked() locked}? 172 */ 173 public boolean isLocked() { 174 return myImmutable; 175 } 176 177 public boolean isRecurse() { 178 return myIterate; 179 } 180 181 /** 182 * Should this include recurse 183 * 184 * @return Returns a reference to <code>this</code> for easy method chaining 185 */ 186 public Include setRecurse(boolean theRecurse) { 187 myIterate = theRecurse; 188 return this; 189 } 190 191 public void setValue(String theValue) { 192 if (myImmutable) { 193 throw new IllegalStateException(Msg.code(1888) + "Can not change the value of this include"); 194 } 195 196 String value = defaultString(theValue); 197 198 int firstColon = value.indexOf(':'); 199 String paramType; 200 String paramName; 201 String paramTargetType; 202 if (firstColon == -1 || firstColon == value.length() - 1) { 203 paramType = null; 204 paramName = null; 205 paramTargetType = null; 206 } else { 207 paramType = value.substring(0, firstColon); 208 int secondColon = value.indexOf(':', firstColon + 1); 209 if (secondColon == -1) { 210 paramName = value.substring(firstColon + 1); 211 paramTargetType = null; 212 } else { 213 paramName = value.substring(firstColon + 1, secondColon); 214 paramTargetType = value.substring(secondColon + 1); 215 } 216 } 217 218 myParamType = paramType; 219 myParamName = paramName; 220 myParamTargetType = paramTargetType; 221 myValue = theValue; 222 223 } 224 225 /** 226 * Return a new 227 */ 228 public Include toLocked() { 229 Include retVal = new Include(myValue, myIterate, true); 230 return retVal; 231 } 232 233 @Override 234 public String toString() { 235 ToStringBuilder builder = new ToStringBuilder(this); 236 builder.append("value", myValue); 237 builder.append("iterate", myIterate); 238 return builder.toString(); 239 } 240 241 /** 242 * Creates and returns a new copy of this Include with the given type. The following table shows what will be 243 * returned: 244 * <table> 245 * <tr> 246 * <th>Initial Contents</th> 247 * <th>theResourceType</th> 248 * <th>Output</th> 249 * </tr> 250 * <tr> 251 * <td>Patient:careProvider</th> 252 * <th>Organization</th> 253 * <th>Patient:careProvider:Organization</th> 254 * </tr> 255 * <tr> 256 * <td>Patient:careProvider:Practitioner</th> 257 * <th>Organization</th> 258 * <th>Patient:careProvider:Organization</th> 259 * </tr> 260 * <tr> 261 * <td>Patient</th> 262 * <th>(any)</th> 263 * <th>{@link IllegalStateException}</th> 264 * </tr> 265 * </table> 266 * 267 * @param theResourceType 268 * The resource type (e.g. "Organization") 269 * @return A new copy of the include. Note that if this include is {@link #toLocked() locked}, the returned include 270 * will be too 271 */ 272 public Include withType(String theResourceType) { 273 StringBuilder b = new StringBuilder(); 274 275 String paramType = getParamType(); 276 String paramName = getParamName(); 277 if (isBlank(paramType) || isBlank(paramName)) { 278 throw new IllegalStateException(Msg.code(1889) + "This include does not contain a value in the format [ResourceType]:[paramName]"); 279 } 280 b.append(paramType); 281 b.append(":"); 282 b.append(paramName); 283 284 if (isNotBlank(theResourceType)) { 285 b.append(':'); 286 b.append(theResourceType); 287 } 288 Include retVal = new Include(b.toString(), myIterate, myImmutable); 289 return retVal; 290 } 291 292}