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